From 15555248a081dbd4ad6486b917c26a885c558b18 Mon Sep 17 00:00:00 2001 From: sztyr <54115527+sztyr@users.noreply.github.com> Date: Fri, 19 Nov 2021 01:26:31 +0100 Subject: [PATCH 01/30] Add the android.permission.ACTIVITY_RECOGNITION setup to the README --- packages/health/README.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/packages/health/README.md b/packages/health/README.md index 32692b9bc..5b683a57f 100644 --- a/packages/health/README.md +++ b/packages/health/README.md @@ -94,6 +94,27 @@ Follow the instructions at https://console.developers.google.com/flows/enableapi The client id will look something like `YOUR_CLIENT_ID.apps.googleusercontent.com` +### Android Permissions + +Starting from API level 28 (Android 9.0) acessing some fitness data (e.g. Steps) requires a special permission. + +To set it add the following line to your `AndroidManifest.xml` file. +``` + +``` + +There's a `debug`, `main` and `profile` version which are chosen depending on how you start your app. In general, it's sufficient to add permission only to the `main` version. + +Beacuse this is a `dangerous` protectionLevel permission system will not grant it automaticlly and it requires user action. + +You can prompt the user for it using the [permission_handler](https://pub.dev/packages/permission_handler) plugin. +Follow the plugin setup instructions and add the following line before requsting the data: + + ``` + await Permission.activityRecognition.request(); + ``` + + ### Android X Replace the content of the `android/gradle.properties` file with the following lines: From 50f3955b11a3e80f3a58f360106fce8f39c2e166 Mon Sep 17 00:00:00 2001 From: PinkyUni Date: Fri, 3 Dec 2021 11:33:04 +0300 Subject: [PATCH 02/30] feat: Add method for calculating total steps --- .../cachet/plugins/health/HealthPlugin.kt | 85 +++++++++++++++++++ .../example/.flutter-plugins-dependencies | 2 +- .../ios/Classes/SwiftHealthPlugin.swift | 41 +++++++++ packages/health/lib/src/health_factory.dart | 16 ++++ 4 files changed, 143 insertions(+), 1 deletion(-) diff --git a/packages/health/android/src/main/kotlin/cachet/plugins/health/HealthPlugin.kt b/packages/health/android/src/main/kotlin/cachet/plugins/health/HealthPlugin.kt index 12c76b0a4..09ed16712 100644 --- a/packages/health/android/src/main/kotlin/cachet/plugins/health/HealthPlugin.kt +++ b/packages/health/android/src/main/kotlin/cachet/plugins/health/HealthPlugin.kt @@ -420,12 +420,97 @@ class HealthPlugin(private var channel: MethodChannel? = null) : MethodCallHandl } } + fun getTotalStepsInInterval(call: MethodCall, result: Result) { + val start = call.argument("startDate")!! + val end = call.argument("endDate")!! + + getStepsInRange(start, end) { map: Map?, e: Throwable? -> + if (map != null) { + assert(map.size <= 1) { "getTotalStepsInInterval should return only one interval. Found: ${map.size}" } + result.success(map.values.firstOrNull()) + } else { + result.error("failed", e?.message, null) + } + } + } + + private fun getStepsInRange( + start: Long, + end: Long, + result: (Map?, Throwable?) -> Unit + ) { + val activity = activity ?: return + + val stepsDataType = keyToHealthDataType(STEPS) + val aggregatedDataType = keyToHealthDataType(AGGREGATE_STEP_COUNT) + + val fitnessOptions = FitnessOptions.builder() + .addDataType(stepsDataType) + .addDataType(aggregatedDataType) + .build() + val gsa = GoogleSignIn.getAccountForExtension(activity, fitnessOptions) + + val ds = DataSource.Builder() + .setAppPackageName("com.google.android.gms") + .setDataType(stepsDataType) + .setType(DataSource.TYPE_DERIVED) + .setStreamName("estimated_steps") + .build() + + val duration = (end - start).toInt() + + val request = DataReadRequest.Builder() + .aggregate(ds) + .bucketByTime(duration, TimeUnit.MILLISECONDS) + .setTimeRange(start, end, TimeUnit.MILLISECONDS) + .build() + + val response = Fitness.getHistoryClient(activity, gsa).readData(request) + + Thread { + try { + val readDataResult = Tasks.await(response) + + val map = HashMap() // need to return to Dart so can't use sparse array + for (bucket in readDataResult.buckets) { + val dp = bucket.dataSets.firstOrNull()?.dataPoints?.firstOrNull() + if (dp != null) { + print(dp) + + val count = dp.getValue(aggregatedDataType.fields[0]) + + val startTime = dp.getStartTime(TimeUnit.MILLISECONDS) + val startDate = Date(startTime) + val endDate = Date(dp.getEndTime(TimeUnit.MILLISECONDS)) + Log.i("FLUTTER_HEALTH::SUCCESS", "returning $count steps for $startDate - $endDate") + map[startTime] = count.asInt() + } else { + val startDay = Date(start) + val endDay = Date(end) + Log.i("FLUTTER_HEALTH::ERROR", "no steps for $startDay - $endDay") + } + } + activity.runOnUiThread { + result(map, null) + } + } catch (e: Throwable) { + Log.e("FLUTTER_HEALTH::ERROR", "failed: ${e.message}") + + activity.runOnUiThread { + result(null, e) + } + } + + }.start() + } + /// Handle calls from the MethodChannel override fun onMethodCall(call: MethodCall, result: Result) { when (call.method) { "requestAuthorization" -> requestAuthorization(call, result) "getData" -> getData(call, result) "writeData" -> writeData(call, result) + "getTotalStepsInInterval" -> getTotalStepsInInterval(call, result) else -> result.notImplemented() } } diff --git a/packages/health/example/.flutter-plugins-dependencies b/packages/health/example/.flutter-plugins-dependencies index aac5d2cc2..37ecaab6b 100644 --- a/packages/health/example/.flutter-plugins-dependencies +++ b/packages/health/example/.flutter-plugins-dependencies @@ -1 +1 @@ -{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"device_info_plus","path":"/Users/bardram/.pub-cache/hosted/pub.dartlang.org/device_info_plus-3.1.1/","dependencies":[]},{"name":"health","path":"/Users/bardram/dev/flutter-plugins/packages/health/","dependencies":["device_info_plus"]}],"android":[{"name":"device_info_plus","path":"/Users/bardram/.pub-cache/hosted/pub.dartlang.org/device_info_plus-3.1.1/","dependencies":[]},{"name":"health","path":"/Users/bardram/dev/flutter-plugins/packages/health/","dependencies":["device_info_plus"]}],"macos":[{"name":"device_info_plus_macos","path":"/Users/bardram/.pub-cache/hosted/pub.dartlang.org/device_info_plus_macos-2.2.0/","dependencies":[]}],"linux":[],"windows":[],"web":[{"name":"device_info_plus_web","path":"/Users/bardram/.pub-cache/hosted/pub.dartlang.org/device_info_plus_web-2.1.0/","dependencies":[]}]},"dependencyGraph":[{"name":"device_info_plus","dependencies":["device_info_plus_macos","device_info_plus_web"]},{"name":"device_info_plus_macos","dependencies":[]},{"name":"device_info_plus_web","dependencies":[]},{"name":"health","dependencies":["device_info_plus"]}],"date_created":"2021-11-18 09:57:35.099678","version":"2.5.3"} \ No newline at end of file +{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"device_info_plus","path":"D:\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\device_info_plus-3.1.1\\\\","dependencies":[]},{"name":"health","path":"D:\\\\SheepApps\\\\Flutter\\\\flutter-plugins\\\\packages\\\\health\\\\","dependencies":["device_info_plus"]}],"android":[{"name":"device_info_plus","path":"D:\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\device_info_plus-3.1.1\\\\","dependencies":[]},{"name":"health","path":"D:\\\\SheepApps\\\\Flutter\\\\flutter-plugins\\\\packages\\\\health\\\\","dependencies":["device_info_plus"]}],"macos":[{"name":"device_info_plus_macos","path":"D:\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\device_info_plus_macos-2.2.0\\\\","dependencies":[]}],"linux":[],"windows":[],"web":[{"name":"device_info_plus_web","path":"D:\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\device_info_plus_web-2.1.0\\\\","dependencies":[]}]},"dependencyGraph":[{"name":"device_info_plus","dependencies":["device_info_plus_macos","device_info_plus_web"]},{"name":"device_info_plus_macos","dependencies":[]},{"name":"device_info_plus_web","dependencies":[]},{"name":"health","dependencies":["device_info_plus"]}],"date_created":"2021-12-03 11:04:00.730455","version":"2.5.3"} \ No newline at end of file diff --git a/packages/health/ios/Classes/SwiftHealthPlugin.swift b/packages/health/ios/Classes/SwiftHealthPlugin.swift index 9aa8dc264..c7930a947 100644 --- a/packages/health/ios/Classes/SwiftHealthPlugin.swift +++ b/packages/health/ios/Classes/SwiftHealthPlugin.swift @@ -73,6 +73,11 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { getData(call: call, result: result) } + /// Handle getTotalStepsInInterval + else if (call.method.elementsEqual("getTotalStepsInInterval")){ + getTotalStepsInInterval(call: call, result: result) + } + /// Handle writeData else if (call.method.elementsEqual("writeData")){ writeData(call: call, result: result) @@ -216,6 +221,42 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { HKHealthStore().execute(query) } + func getTotalStepsInInterval(call: FlutterMethodCall, result: @escaping FlutterResult) { + let arguments = call.arguments as! [String: Int] + let startMillis = arguments["startDate"]! + let endMillis = arguments["endDate"]! + + let startDate = Date(timeIntervalSince1970: startMillis.toTimeInterval) + let endDate = Date(timeIntervalSince1970: endMillis.toTimeInterval) + let sampleType = dataTypeLookUp(key: STEPS) + let predicate = HKQuery.predicateForSamples(withStart: startDate, end: endDate, options: [.strictStartDate]) + + let query = HKStatisticsQuery(quantityType: sampleType, + quantitySamplePredicate: predicate, + options: .cumulativeSum) { query, queryResult, error in + + guard let queryResult = queryResult else { + let error = error! as NSError + print("[getTotalStepsInInterval] got error: \(error)") + result(FlutterError(code: "\(error.code)", message: error.domain, details: error.localizedDescription)) + return + } + + var steps = 0.0 + + if let quantity = queryResult.sumQuantity() { + let unit = HKUnit.count() + steps = quantity.doubleValue(for: unit) + print("Amount of steps: \(steps), since: \(queryResult.startDate) until: \(queryResult.endDate)") + } + + let totalSteps = Int(steps) + result(totalSteps) + } + + HKHealthStore().execute(query) + } + func unitLookUp(key: String) -> HKUnit { guard let unit = unitDict[key] else { return HKUnit.count() diff --git a/packages/health/lib/src/health_factory.dart b/packages/health/lib/src/health_factory.dart index 3bcbc43fd..11c0f8029 100644 --- a/packages/health/lib/src/health_factory.dart +++ b/packages/health/lib/src/health_factory.dart @@ -214,4 +214,20 @@ class HealthFactory { } return unique; } + + Future> getTotalStepsCount( + DateTime startDate, + DateTime endDate, + ) async { + // Set parameters for method channel request + final args = { + 'startDate': startDate.millisecondsSinceEpoch, + 'endDate': endDate.millisecondsSinceEpoch + }; + final stepsCount = await _channel.invokeMethod( + 'getTotalStepsInInterval', + args, + ); + return stepsCount; + } } From e324a9e79041d3a3872202b570edcfa5e0f16128 Mon Sep 17 00:00:00 2001 From: PinkyUni Date: Fri, 3 Dec 2021 11:49:04 +0300 Subject: [PATCH 03/30] feat: Fix return type --- packages/health/lib/src/health_factory.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/health/lib/src/health_factory.dart b/packages/health/lib/src/health_factory.dart index 11c0f8029..5e0d1b862 100644 --- a/packages/health/lib/src/health_factory.dart +++ b/packages/health/lib/src/health_factory.dart @@ -215,7 +215,7 @@ class HealthFactory { return unique; } - Future> getTotalStepsCount( + Future getTotalStepsCount( DateTime startDate, DateTime endDate, ) async { From f984cf7f8c952f7446a7cdcf7f10a14400c104d8 Mon Sep 17 00:00:00 2001 From: PinkyUni Date: Fri, 3 Dec 2021 11:49:42 +0300 Subject: [PATCH 04/30] feat: Rename method --- packages/health/lib/src/health_factory.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/health/lib/src/health_factory.dart b/packages/health/lib/src/health_factory.dart index 5e0d1b862..3a0a9330a 100644 --- a/packages/health/lib/src/health_factory.dart +++ b/packages/health/lib/src/health_factory.dart @@ -215,7 +215,7 @@ class HealthFactory { return unique; } - Future getTotalStepsCount( + Future getTotalStepsInInterval( DateTime startDate, DateTime endDate, ) async { From 2306c3f810d941cf55feea3c3967f783b9cfa0cc Mon Sep 17 00:00:00 2001 From: PinkyUni Date: Fri, 3 Dec 2021 12:24:48 +0300 Subject: [PATCH 05/30] feat: Fix issues --- .../src/main/kotlin/cachet/plugins/health/HealthPlugin.kt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/health/android/src/main/kotlin/cachet/plugins/health/HealthPlugin.kt b/packages/health/android/src/main/kotlin/cachet/plugins/health/HealthPlugin.kt index 09ed16712..629102b68 100644 --- a/packages/health/android/src/main/kotlin/cachet/plugins/health/HealthPlugin.kt +++ b/packages/health/android/src/main/kotlin/cachet/plugins/health/HealthPlugin.kt @@ -18,6 +18,7 @@ import android.util.Log import androidx.annotation.NonNull import io.flutter.plugin.common.PluginRegistry.ActivityResultListener import java.util.concurrent.TimeUnit +import java.util.Date import kotlin.concurrent.thread import com.google.android.gms.fitness.data.* import com.google.android.gms.fitness.request.SessionReadRequest @@ -39,6 +40,7 @@ class HealthPlugin(private var channel: MethodChannel? = null) : MethodCallHandl private var HEIGHT = "HEIGHT" private var WEIGHT = "WEIGHT" private var STEPS = "STEPS" + private var AGGREGATE_STEP_COUNT = "AGGREGATE_STEP_COUNT" private var ACTIVE_ENERGY_BURNED = "ACTIVE_ENERGY_BURNED" private var HEART_RATE = "HEART_RATE" private var BODY_TEMPERATURE = "BODY_TEMPERATURE" @@ -141,6 +143,7 @@ class HealthPlugin(private var channel: MethodChannel? = null) : MethodCallHandl HEIGHT -> DataType.TYPE_HEIGHT WEIGHT -> DataType.TYPE_WEIGHT STEPS -> DataType.TYPE_STEP_COUNT_DELTA + AGGREGATE_STEP_COUNT -> DataType.AGGREGATE_STEP_COUNT_DELTA ACTIVE_ENERGY_BURNED -> DataType.TYPE_CALORIES_EXPENDED HEART_RATE -> DataType.TYPE_HEART_RATE_BPM BODY_TEMPERATURE -> HealthDataTypes.TYPE_BODY_TEMPERATURE From c34b08ee33a63b9b400a941fbe3af68c4af0df5f Mon Sep 17 00:00:00 2001 From: PinkyUni Date: Fri, 3 Dec 2021 13:20:30 +0300 Subject: [PATCH 06/30] feat: Fix ios issues --- .../health/ios/Classes/SwiftHealthPlugin.swift | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/packages/health/ios/Classes/SwiftHealthPlugin.swift b/packages/health/ios/Classes/SwiftHealthPlugin.swift index c7930a947..4631b9f2c 100644 --- a/packages/health/ios/Classes/SwiftHealthPlugin.swift +++ b/packages/health/ios/Classes/SwiftHealthPlugin.swift @@ -223,13 +223,15 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { func getTotalStepsInInterval(call: FlutterMethodCall, result: @escaping FlutterResult) { let arguments = call.arguments as! [String: Int] - let startMillis = arguments["startDate"]! - let endMillis = arguments["endDate"]! + let startDate = (arguments?["startDate"] as? NSNumber) ?? 0 + let endDate = (arguments?["endDate"] as? NSNumber) ?? 0 + + // Convert dates from milliseconds to Date() + let dateFrom = Date(timeIntervalSince1970: startDate.doubleValue / 1000) + let dateTo = Date(timeIntervalSince1970: endDate.doubleValue / 1000) - let startDate = Date(timeIntervalSince1970: startMillis.toTimeInterval) - let endDate = Date(timeIntervalSince1970: endMillis.toTimeInterval) let sampleType = dataTypeLookUp(key: STEPS) - let predicate = HKQuery.predicateForSamples(withStart: startDate, end: endDate, options: [.strictStartDate]) + let predicate = HKQuery.predicateForSamples(withStart: dateFrom, end: dateTo, options: .strictStartDate) let query = HKStatisticsQuery(quantityType: sampleType, quantitySamplePredicate: predicate, @@ -237,7 +239,6 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { guard let queryResult = queryResult else { let error = error! as NSError - print("[getTotalStepsInInterval] got error: \(error)") result(FlutterError(code: "\(error.code)", message: error.domain, details: error.localizedDescription)) return } @@ -247,7 +248,7 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { if let quantity = queryResult.sumQuantity() { let unit = HKUnit.count() steps = quantity.doubleValue(for: unit) - print("Amount of steps: \(steps), since: \(queryResult.startDate) until: \(queryResult.endDate)") + print("Amount of steps: \(steps)") } let totalSteps = Int(steps) From 9726e6d4f11a4b6ccfec8022a7f52a4128d46263 Mon Sep 17 00:00:00 2001 From: PinkyUni Date: Fri, 3 Dec 2021 16:59:11 +0300 Subject: [PATCH 07/30] feat: Fix some swift issues --- packages/health/ios/Classes/SwiftHealthPlugin.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/health/ios/Classes/SwiftHealthPlugin.swift b/packages/health/ios/Classes/SwiftHealthPlugin.swift index 4631b9f2c..d11fb9995 100644 --- a/packages/health/ios/Classes/SwiftHealthPlugin.swift +++ b/packages/health/ios/Classes/SwiftHealthPlugin.swift @@ -222,7 +222,7 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { } func getTotalStepsInInterval(call: FlutterMethodCall, result: @escaping FlutterResult) { - let arguments = call.arguments as! [String: Int] + let arguments = call.arguments as? NSDictionary let startDate = (arguments?["startDate"] as? NSNumber) ?? 0 let endDate = (arguments?["endDate"] as? NSNumber) ?? 0 @@ -230,7 +230,7 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { let dateFrom = Date(timeIntervalSince1970: startDate.doubleValue / 1000) let dateTo = Date(timeIntervalSince1970: endDate.doubleValue / 1000) - let sampleType = dataTypeLookUp(key: STEPS) + let sampleType = HKQuantityType.quantityType(forIdentifier: .stepCount)! let predicate = HKQuery.predicateForSamples(withStart: dateFrom, end: dateTo, options: .strictStartDate) let query = HKStatisticsQuery(quantityType: sampleType, From a924c3fda82616a72df8de0a07b13834a2ade7e4 Mon Sep 17 00:00:00 2001 From: altanod Date: Tue, 14 Dec 2021 11:11:43 +0800 Subject: [PATCH 08/30] fix: android 12 intent-flag --- .../ActivityRecognitionFlutterPlugin.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/activity_recognition_flutter/android/src/main/java/dk/cachet/activity_recognition_flutter/ActivityRecognitionFlutterPlugin.java b/packages/activity_recognition_flutter/android/src/main/java/dk/cachet/activity_recognition_flutter/ActivityRecognitionFlutterPlugin.java index 67d70be7f..9f02e5dcb 100644 --- a/packages/activity_recognition_flutter/android/src/main/java/dk/cachet/activity_recognition_flutter/ActivityRecognitionFlutterPlugin.java +++ b/packages/activity_recognition_flutter/android/src/main/java/dk/cachet/activity_recognition_flutter/ActivityRecognitionFlutterPlugin.java @@ -45,7 +45,11 @@ public class ActivityRecognitionFlutterPlugin implements FlutterPlugin, EventCha private void startActivityTracking() { // Start the service Intent intent = new Intent(androidActivity, ActivityRecognizedBroadcastReceiver.class); - PendingIntent pendingIntent = PendingIntent.getBroadcast(androidActivity, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT); + int flags = PendingIntent.FLAG_UPDATE_CURRENT; + if (Build.VERSION.SDK_INT >= 23) { + flags |= PendingIntent.FLAG_IMMUTABLE; + } + PendingIntent pendingIntent = PendingIntent.getBroadcast(androidActivity, 0, intent, flags); // Frequency in milliseconds long frequency = 5 * 1000; From 1234a70d7a26d09118c8defefc4368b3bc3fe4b1 Mon Sep 17 00:00:00 2001 From: bardram Date: Wed, 29 Dec 2021 18:08:40 +0100 Subject: [PATCH 09/30] merge of PR #457 and #462 --- packages/health/CHANGELOG.md | 4 ++++ .../main/kotlin/cachet/plugins/health/HealthPlugin.kt | 9 --------- 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/packages/health/CHANGELOG.md b/packages/health/CHANGELOG.md index dd43a2865..aa59d17b4 100644 --- a/packages/health/CHANGELOG.md +++ b/packages/health/CHANGELOG.md @@ -1,3 +1,7 @@ +## 3.3.2 +* [PR #457](https://github.com/cph-cachet/flutter-plugins/pull/457) - Add sleep in bed to android +* [PR #462](https://github.com/cph-cachet/flutter-plugins/pull/462) - Fixed (regression) issues with metric and permissions + ## 3.3.1 * [PR #428](https://github.com/cph-cachet/flutter-plugins/pull/428) - DISTANCE_DELTA is for Android, not iOS * [PR #454](https://github.com/cph-cachet/flutter-plugins/pull/454) - added missing READ_ACCESS diff --git a/packages/health/android/src/main/kotlin/cachet/plugins/health/HealthPlugin.kt b/packages/health/android/src/main/kotlin/cachet/plugins/health/HealthPlugin.kt index 5f7d03459..8094ab567 100644 --- a/packages/health/android/src/main/kotlin/cachet/plugins/health/HealthPlugin.kt +++ b/packages/health/android/src/main/kotlin/cachet/plugins/health/HealthPlugin.kt @@ -423,7 +423,6 @@ class HealthPlugin(private var channel: MethodChannel? = null) : MethodCallHandl private fun callToHealthTypes(call: MethodCall): FitnessOptions { val typesBuilder = FitnessOptions.builder() val args = call.arguments as HashMap<*, *> -//<<<<<<< metric val types = (args["types"] as? ArrayList<*>)?.filterIsInstance() val permissions = (args["permissions"] as? ArrayList<*>)?.filterIsInstance() @@ -443,15 +442,7 @@ class HealthPlugin(private var channel: MethodChannel? = null) : MethodCallHandl } else -> throw IllegalArgumentException("Unknown access type $access") } - if (typeKey == SLEEP_ASLEEP || typeKey == SLEEP_AWAKE) { -//======= - val types = args["types"] as ArrayList<*> - for (typeKey in types) { - if (typeKey !is String) continue - typesBuilder.addDataType(keyToHealthDataType(typeKey), FitnessOptions.ACCESS_READ) - typesBuilder.addDataType(keyToHealthDataType(typeKey), FitnessOptions.ACCESS_WRITE) if (typeKey == SLEEP_ASLEEP || typeKey == SLEEP_AWAKE || typeKey == SLEEP_IN_BED) { -//>>>>>>> master typesBuilder.accessSleepSessions(FitnessOptions.ACCESS_READ) when (access) { 0 -> typesBuilder.accessSleepSessions(FitnessOptions.ACCESS_READ) From 765796cdecef60e1ec08af7dc6308f5e727697f6 Mon Sep 17 00:00:00 2001 From: bardram Date: Wed, 29 Dec 2021 21:13:58 +0100 Subject: [PATCH 10/30] Update of example app and API docs --- .gitignore | 1 + packages/health/CHANGELOG.md | 6 +- packages/health/README.md | 61 ++++-- .../example/.flutter-plugins-dependencies | 2 +- .../ios/Flutter/flutter_export_environment.sh | 11 +- .../ios/Runner.xcodeproj/project.pbxproj | 10 +- .../ios/Runner/RunnerDebug.entitlements | 10 + packages/health/example/lib/main.dart | 187 +++++++++++------- packages/health/example/pubspec.yaml | 1 + .../ios/Classes/SwiftHealthPlugin.swift | 1 - packages/health/lib/src/data_types.dart | 1 + packages/health/lib/src/health_factory.dart | 73 +++---- packages/health/pubspec.yaml | 2 +- 13 files changed, 235 insertions(+), 131 deletions(-) create mode 100644 packages/health/example/ios/Runner/RunnerDebug.entitlements diff --git a/.gitignore b/.gitignore index 7ca4f00c7..86e4f2f17 100644 --- a/.gitignore +++ b/.gitignore @@ -122,3 +122,4 @@ packages/audio_streamer/example/.flutter-plugins-dependencies *.sh packages/health/example/.flutter-plugins-dependencies *.sh +packages/health/example/.flutter-plugins-dependencies diff --git a/packages/health/CHANGELOG.md b/packages/health/CHANGELOG.md index aa59d17b4..5451eba4e 100644 --- a/packages/health/CHANGELOG.md +++ b/packages/health/CHANGELOG.md @@ -1,6 +1,10 @@ -## 3.3.2 +## 3.4.0 * [PR #457](https://github.com/cph-cachet/flutter-plugins/pull/457) - Add sleep in bed to android +* [PR #458](https://github.com/cph-cachet/flutter-plugins/pull/458) - Add the android.permission.ACTIVITY_RECOGNITION setup to the README * [PR #462](https://github.com/cph-cachet/flutter-plugins/pull/462) - Fixed (regression) issues with metric and permissions +* [PR #471](https://github.com/cph-cachet/flutter-plugins/pull/471) - Get total steps +* update of example app to refelct new features. +* update of API documentation. ## 3.3.1 * [PR #428](https://github.com/cph-cachet/flutter-plugins/pull/428) - DISTANCE_DELTA is for Android, not iOS diff --git a/packages/health/README.md b/packages/health/README.md index 5b683a57f..bbec641ce 100644 --- a/packages/health/README.md +++ b/packages/health/README.md @@ -1,14 +1,12 @@ # Health -This library combines both GoogleFit and AppleHealthKit. It support most of the values provided. +Enables reading and writing health data from/to Apple Health and Google Fit. Also allow for getting the total number of steps for a specific time period. -Supports **iOS** and **Android X** - -NB: For Android, your app _needs_ to have Google Fit installed and have access to the internet, otherwise this plugin will not work. +> For Android, the target phone __needs__ to have [Google Fit](https://www.google.com/fit/) installed and have access to the internet, otherwise this plugin will not work. ## Data Types -| **Data Type** | **Unit** | **iOS** | **Android** | **Comments** | +| **Data Type** | **Unit** | **iOS** | **Android** | **Comments** | | --------------------------- | ----------------------- | ----------- | ------------ | ----------------------------------------------------------- | | ACTIVE_ENERGY_BURNED | CALORIES | yes | yes | | | BASAL_ENERGY_BURNED | CALORIES | yes | | | @@ -46,7 +44,7 @@ NB: For Android, your app _needs_ to have Google Fit installed and have access t ## Setup -### Apple HealthKit (iOS) +### Apple Health (iOS) Step 1: Append the Info.plist with the following 2 entries @@ -92,21 +90,21 @@ Certificate fingerprints: Follow the instructions at https://console.developers.google.com/flows/enableapi?apiid=fitness for setting up an OAuth2 Client ID for a Google project, and adding the SHA1 fingerprint to that OAuth2 credential. -The client id will look something like `YOUR_CLIENT_ID.apps.googleusercontent.com` +The client id will look something like `YOUR_CLIENT_ID.apps.googleusercontent.com`. ### Android Permissions Starting from API level 28 (Android 9.0) acessing some fitness data (e.g. Steps) requires a special permission. To set it add the following line to your `AndroidManifest.xml` file. + ``` ``` There's a `debug`, `main` and `profile` version which are chosen depending on how you start your app. In general, it's sufficient to add permission only to the `main` version. -Beacuse this is a `dangerous` protectionLevel permission system will not grant it automaticlly and it requires user action. - +Beacuse this is labled as a `dangerous` protection level, the permission system will not grant it automaticlly and it requires the user's action. You can prompt the user for it using the [permission_handler](https://pub.dev/packages/permission_handler) plugin. Follow the plugin setup instructions and add the following line before requsting the data: @@ -127,9 +125,50 @@ android.useAndroidX=true ## Usage -Below is a snippet from the `example app` showing the plugin in use. +See the example app for detailed examples of how to use the Health API. + +The Health plugin is used via the `HealthFactory` class using the different methods for handling permissions and getting and adding data to Apple Health / Google Fit. +Below is a simplified flow of how to use the plugin. + +```dart + // create a HealthFactory for use in the app + HealthFactory health = HealthFactory(); + + // define the types to get + var types = [ + HealthDataType.STEPS, + HealthDataType.WEIGHT, + HealthDataType.HEIGHT, + HealthDataType.BLOOD_GLUCOSE, + ]; + + // requesting access to the data types before reading them + bool requested = await health.requestAuthorization(types); + + var now = DateTime.now(); + + // fetch health data from the last 24 hours + List healthData = await health.getHealthDataFromTypes( + now.subtract(Duration(days: 1)), now, types); + + // request permissions to write steps and blood glucose + types = [HealthDataType.STEPS, HealthDataType.BLOOD_GLUCOSE]; + var permissions = [ + HealthDataAccess.READ_WRITE, + HealthDataAccess.READ_WRITE + ]; + await health.requestAuthorization(types, permissions: permissions); + + // write steps and blood glucose + bool success = await health.writeHealthData(10, HealthDataType.STEPS, now, now); + success = await health.writeHealthData(3.1, HealthDataType.BLOOD_GLUCOSE, now, now); + + // get the number of steps for today + var midnight = DateTime(now.year, now.month, now.day); + int? steps = await health.getTotalStepsInInterval(midnight, now); +``` -### Health data +### Health Data A `HealthData` object contains the following data fields: diff --git a/packages/health/example/.flutter-plugins-dependencies b/packages/health/example/.flutter-plugins-dependencies index 37ecaab6b..ad5314445 100644 --- a/packages/health/example/.flutter-plugins-dependencies +++ b/packages/health/example/.flutter-plugins-dependencies @@ -1 +1 @@ -{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"device_info_plus","path":"D:\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\device_info_plus-3.1.1\\\\","dependencies":[]},{"name":"health","path":"D:\\\\SheepApps\\\\Flutter\\\\flutter-plugins\\\\packages\\\\health\\\\","dependencies":["device_info_plus"]}],"android":[{"name":"device_info_plus","path":"D:\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\device_info_plus-3.1.1\\\\","dependencies":[]},{"name":"health","path":"D:\\\\SheepApps\\\\Flutter\\\\flutter-plugins\\\\packages\\\\health\\\\","dependencies":["device_info_plus"]}],"macos":[{"name":"device_info_plus_macos","path":"D:\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\device_info_plus_macos-2.2.0\\\\","dependencies":[]}],"linux":[],"windows":[],"web":[{"name":"device_info_plus_web","path":"D:\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\device_info_plus_web-2.1.0\\\\","dependencies":[]}]},"dependencyGraph":[{"name":"device_info_plus","dependencies":["device_info_plus_macos","device_info_plus_web"]},{"name":"device_info_plus_macos","dependencies":[]},{"name":"device_info_plus_web","dependencies":[]},{"name":"health","dependencies":["device_info_plus"]}],"date_created":"2021-12-03 11:04:00.730455","version":"2.5.3"} \ No newline at end of file +{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"device_info_plus","path":"/Users/bardram/.pub-cache/hosted/pub.dartlang.org/device_info_plus-3.1.1/","dependencies":[]},{"name":"health","path":"/Users/bardram/dev/flutter-plugins/packages/health/","dependencies":["device_info_plus"]}],"android":[{"name":"device_info_plus","path":"/Users/bardram/.pub-cache/hosted/pub.dartlang.org/device_info_plus-3.1.1/","dependencies":[]},{"name":"health","path":"/Users/bardram/dev/flutter-plugins/packages/health/","dependencies":["device_info_plus"]}],"macos":[{"name":"device_info_plus_macos","path":"/Users/bardram/.pub-cache/hosted/pub.dartlang.org/device_info_plus_macos-2.2.0/","dependencies":[]}],"linux":[],"windows":[],"web":[{"name":"device_info_plus_web","path":"/Users/bardram/.pub-cache/hosted/pub.dartlang.org/device_info_plus_web-2.1.0/","dependencies":[]}]},"dependencyGraph":[{"name":"device_info_plus","dependencies":["device_info_plus_macos","device_info_plus_web"]},{"name":"device_info_plus_macos","dependencies":[]},{"name":"device_info_plus_web","dependencies":[]},{"name":"health","dependencies":["device_info_plus"]}],"date_created":"2021-12-29 20:33:27.370175","version":"2.5.3"} \ No newline at end of file diff --git a/packages/health/example/ios/Flutter/flutter_export_environment.sh b/packages/health/example/ios/Flutter/flutter_export_environment.sh index c00f81a61..671cbf9db 100755 --- a/packages/health/example/ios/Flutter/flutter_export_environment.sh +++ b/packages/health/example/ios/Flutter/flutter_export_environment.sh @@ -3,11 +3,12 @@ export "FLUTTER_ROOT=/Users/bardram/dev/flutter" export "FLUTTER_APPLICATION_PATH=/Users/bardram/dev/flutter-plugins/packages/health/example" export "COCOAPODS_PARALLEL_CODE_SIGN=true" -export "FLUTTER_TARGET=lib/main.dart" +export "FLUTTER_TARGET=/Users/bardram/dev/flutter-plugins/packages/health/example/lib/main.dart" export "FLUTTER_BUILD_DIR=build" -export "FLUTTER_BUILD_NAME=1.0.0" -export "FLUTTER_BUILD_NUMBER=1" +export "FLUTTER_BUILD_NAME=3.4.0" +export "FLUTTER_BUILD_NUMBER=3.4.0" +export "DART_DEFINES=Zmx1dHRlci5pbnNwZWN0b3Iuc3RydWN0dXJlZEVycm9ycz10cnVl,RkxVVFRFUl9XRUJfQVVUT19ERVRFQ1Q9dHJ1ZQ==" export "DART_OBFUSCATION=false" -export "TRACK_WIDGET_CREATION=false" +export "TRACK_WIDGET_CREATION=true" export "TREE_SHAKE_ICONS=false" -export "PACKAGE_CONFIG=.packages" +export "PACKAGE_CONFIG=/Users/bardram/dev/flutter-plugins/packages/health/example/.dart_tool/package_config.json" diff --git a/packages/health/example/ios/Runner.xcodeproj/project.pbxproj b/packages/health/example/ios/Runner.xcodeproj/project.pbxproj index cfc875d1f..b6fb61701 100644 --- a/packages/health/example/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/health/example/ios/Runner.xcodeproj/project.pbxproj @@ -50,6 +50,7 @@ 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + D6912A28277CD12C0012EA21 /* RunnerDebug.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = RunnerDebug.entitlements; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -107,6 +108,7 @@ 97C146F01CF9000F007C117D /* Runner */ = { isa = PBXGroup; children = ( + D6912A28277CD12C0012EA21 /* RunnerDebug.entitlements */, 82F0E64E2375BDAE0022096E /* Runner.entitlements */, 97C146FA1CF9000F007C117D /* Main.storyboard */, 97C146FD1CF9000F007C117D /* Assets.xcassets */, @@ -174,7 +176,7 @@ TargetAttributes = { 97C146ED1CF9000F007C117D = { CreatedOnToolsVersion = 7.3.1; - DevelopmentTeam = 59TCTNUBMQ; + DevelopmentTeam = 8TB3T6MAZG; LastSwiftMigration = 0910; ProvisioningStyle = Automatic; SystemCapabilities = { @@ -514,11 +516,11 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; - CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; + CODE_SIGN_ENTITLEMENTS = Runner/RunnerDebug.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; - DEVELOPMENT_TEAM = 59TCTNUBMQ; + DEVELOPMENT_TEAM = 8TB3T6MAZG; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", @@ -531,7 +533,7 @@ "$(inherited)", "$(PROJECT_DIR)/Flutter", ); - PRODUCT_BUNDLE_IDENTIFIER = dk.cachet.example; + PRODUCT_BUNDLE_IDENTIFIER = "carp-health-example"; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; diff --git a/packages/health/example/ios/Runner/RunnerDebug.entitlements b/packages/health/example/ios/Runner/RunnerDebug.entitlements new file mode 100644 index 000000000..2ab14a262 --- /dev/null +++ b/packages/health/example/ios/Runner/RunnerDebug.entitlements @@ -0,0 +1,10 @@ + + + + + com.apple.developer.healthkit + + com.apple.developer.healthkit.access + + + diff --git a/packages/health/example/lib/main.dart b/packages/health/example/lib/main.dart index 5b57fef8a..b14045c1a 100644 --- a/packages/health/example/lib/main.dart +++ b/packages/health/example/lib/main.dart @@ -4,11 +4,11 @@ import 'dart:math'; import 'package:flutter/material.dart'; import 'package:health/health.dart'; -void main() => runApp(MyApp()); +void main() => runApp(HealthApp()); -class MyApp extends StatefulWidget { +class HealthApp extends StatefulWidget { @override - _MyAppState createState() => _MyAppState(); + _HealthAppState createState() => _HealthAppState(); } enum AppState { @@ -19,43 +19,106 @@ enum AppState { AUTH_NOT_GRANTED, DATA_ADDED, DATA_NOT_ADDED, + STEPS_READY, } -class _MyAppState extends State { +class _HealthAppState extends State { List _healthDataList = []; AppState _state = AppState.DATA_NOT_FETCHED; int _nofSteps = 10; double _mgdl = 10.0; - @override - void initState() { - super.initState(); + // create a HealthFactory for use in the app + HealthFactory health = HealthFactory(); + + /// Fetch data points from the health plugin and show them in the app. + Future fetchData() async { + setState(() => _state = AppState.FETCHING_DATA); + + // define the types to get + final types = [ + HealthDataType.STEPS, + HealthDataType.WEIGHT, + HealthDataType.HEIGHT, + HealthDataType.BLOOD_GLUCOSE, + // Uncomment this line on iOS - only available on iOS + // HealthDataType.DISTANCE_WALKING_RUNNING, + ]; + + // with coresponsing permissions + final permissions = [ + HealthDataAccess.READ, + HealthDataAccess.READ, + HealthDataAccess.READ, + HealthDataAccess.READ, + ]; + + // get data within the last 24 hours + final now = DateTime.now(); + final yesterday = now.subtract(Duration(days: 1)); + + // requesting access to the data types before reading them + // note that strictly speaking, the [permissions] are not + // needed, since we only want READ access. + bool requested = + await health.requestAuthorization(types, permissions: permissions); + + if (requested) { + try { + // fetch health data + List healthData = + await health.getHealthDataFromTypes(yesterday, now, types); + + // save all the new data points (only the first 100) + _healthDataList.addAll((healthData.length < 100) + ? healthData + : healthData.sublist(0, 100)); + } catch (error) { + print("Exception in getHealthDataFromTypes: $error"); + } + + // filter out duplicates + _healthDataList = HealthFactory.removeDuplicates(_healthDataList); + + // print the results + _healthDataList.forEach((x) => print(x)); + + // update the UI to display the results + setState(() { + _state = + _healthDataList.isEmpty ? AppState.NO_DATA : AppState.DATA_READY; + }); + } else { + print("Authorization not granted"); + setState(() => _state = AppState.DATA_NOT_FETCHED); + } } + /// Add some random health data. Future addData() async { - HealthFactory health = HealthFactory(); - - final time = DateTime.now(); - final ago = time.add(Duration(minutes: -5)); + final now = DateTime.now(); + final earlier = now.subtract(Duration(minutes: 5)); _nofSteps = Random().nextInt(10); final types = [HealthDataType.STEPS, HealthDataType.BLOOD_GLUCOSE]; final rights = [HealthDataAccess.WRITE, HealthDataAccess.WRITE]; - final permissions = [HealthDataAccess.READ_WRITE, HealthDataAccess.READ_WRITE]; + final permissions = [ + HealthDataAccess.READ_WRITE, + HealthDataAccess.READ_WRITE + ]; bool? hasPermissions = await HealthFactory.hasPermissions(types, permissions: rights); if (hasPermissions == false) { await health.requestAuthorization(types, permissions: permissions); } - //_nofSteps = Random().nextInt(10); _mgdl = Random().nextInt(10) * 1.0; bool success = await health.writeHealthData( - _nofSteps.toDouble(), HealthDataType.STEPS, ago, time); + _nofSteps.toDouble(), HealthDataType.STEPS, earlier, now); if (success) { success = await health.writeHealthData( - _mgdl, HealthDataType.BLOOD_GLUCOSE, time, time); + _mgdl, HealthDataType.BLOOD_GLUCOSE, now, now); } setState(() { @@ -63,64 +126,28 @@ class _MyAppState extends State { }); } - /// Fetch data from the healt plugin and print it - Future fetchData() async { - // get everything from midnight until now - DateTime startDate = DateTime(2020, 11, 07, 0, 0, 0); - DateTime endDate = DateTime(2025, 11, 07, 23, 59, 59); - - HealthFactory health = HealthFactory(); - - // define the types to get - List types = [ - HealthDataType.STEPS, - HealthDataType.WEIGHT, - HealthDataType.HEIGHT, - HealthDataType.BLOOD_GLUCOSE, - // Uncomment this line on iOS. This type is supported ONLY on Android! - // HealthDataType.DISTANCE_WALKING_RUNNING, - ]; - - List permissions = [ - HealthDataAccess.READ_WRITE, - HealthDataAccess.READ, - HealthDataAccess.READ, - HealthDataAccess.READ_WRITE, - ]; + /// Fetch steps from the health plugin and show them in the app. + Future fetchStepData() async { + int? steps; - setState(() => _state = AppState.FETCHING_DATA); + // get steps for today (i.e., since midnight) + final now = DateTime.now(); + final midnight = DateTime(now.year, now.month, now.day); - // you MUST request access to the data types before reading them - bool requested = await health.requestAuthorization(types, permissions: permissions); - int steps = 0; + bool requested = await health.requestAuthorization([HealthDataType.STEPS]); if (requested) { try { - // fetch new data - List healthData = - await health.getHealthDataFromTypes(startDate, endDate, types); - - // save all the new data points - _healthDataList.addAll(healthData); - } catch (e) { - print("Caught exception in getHealthDataFromTypes: $e"); + steps = await health.getTotalStepsInInterval(midnight, now); + } catch (error) { + print("Caught exception in getTotalStepsInInterval: $error"); } - // filter out duplicates - _healthDataList = HealthFactory.removeDuplicates(_healthDataList); - - // print the results - _healthDataList.forEach((x) { - print("Data point: $x"); - steps += x.value.round(); - }); - - print("Steps: $steps"); + print('Total number of steps: $steps'); - // update the UI to display the results setState(() { - _state = - _healthDataList.isEmpty ? AppState.NO_DATA : AppState.DATA_READY; + _nofSteps = (steps == null) ? 0 : steps; + _state = (steps == null) ? AppState.NO_DATA : AppState.STEPS_READY; }); } else { print("Authorization not granted"); @@ -163,22 +190,27 @@ class _MyAppState extends State { return Column( children: [ Text('Press the download button to fetch data.'), - Text('Press the plus button to insert some random data.') + Text('Press the plus button to insert some random data.'), + Text('Press the walking button to get total step count.'), ], mainAxisAlignment: MainAxisAlignment.center, ); } Widget _authorizationNotGranted() { - return Text('''Authorization not given. - For Android please check your OAUTH2 client ID is correct in Google Developer Console. - For iOS check your permissions in Apple Health.'''); + return Text('Authorization not given. ' + 'For Android please check your OAUTH2 client ID is correct in Google Developer Console. ' + 'For iOS check your permissions in Apple Health.'); } Widget _dataAdded() { return Text('$_nofSteps steps and $_mgdl mgdl are inserted successfully!'); } + Widget _stepsFetched() { + return Text('Total number of steps: $_nofSteps'); + } + Widget _dataNotAdded() { return Text('Failed to add data'); } @@ -194,6 +226,8 @@ class _MyAppState extends State { return _authorizationNotGranted(); else if (_state == AppState.DATA_ADDED) return _dataAdded(); + else if (_state == AppState.STEPS_READY) + return _stepsFetched(); else if (_state == AppState.DATA_NOT_ADDED) return _dataNotAdded(); return _contentNotFetched(); @@ -204,7 +238,7 @@ class _MyAppState extends State { return MaterialApp( home: Scaffold( appBar: AppBar( - title: const Text('Plugin example app'), + title: const Text('Health Example'), actions: [ IconButton( icon: Icon(Icons.file_download), @@ -213,10 +247,17 @@ class _MyAppState extends State { }, ), IconButton( - onPressed: () { - addData(); - }, - icon: Icon(Icons.add)) + onPressed: () { + addData(); + }, + icon: Icon(Icons.add), + ), + IconButton( + onPressed: () { + fetchStepData(); + }, + icon: Icon(Icons.nordic_walking), + ) ], ), body: Center( diff --git a/packages/health/example/pubspec.yaml b/packages/health/example/pubspec.yaml index fe5d9380a..5005c1c69 100644 --- a/packages/health/example/pubspec.yaml +++ b/packages/health/example/pubspec.yaml @@ -1,6 +1,7 @@ name: health_example description: Demonstrates how to use the health plugin. publish_to: 'none' +version: 3.4.0 environment: sdk: ">=2.12.0-0 <3.0.0" diff --git a/packages/health/ios/Classes/SwiftHealthPlugin.swift b/packages/health/ios/Classes/SwiftHealthPlugin.swift index d406d7a1f..ffab6192e 100644 --- a/packages/health/ios/Classes/SwiftHealthPlugin.swift +++ b/packages/health/ios/Classes/SwiftHealthPlugin.swift @@ -314,7 +314,6 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { if let quantity = queryResult.sumQuantity() { let unit = HKUnit.count() steps = quantity.doubleValue(for: unit) - print("Amount of steps: \(steps)") } let totalSteps = Int(steps) diff --git a/packages/health/lib/src/data_types.dart b/packages/health/lib/src/data_types.dart index 6a50386b9..4eb484a44 100644 --- a/packages/health/lib/src/data_types.dart +++ b/packages/health/lib/src/data_types.dart @@ -48,6 +48,7 @@ enum HealthDataAccess { WRITE, READ_WRITE, } + /// List of data types available on iOS const List _dataTypeKeysIOS = [ HealthDataType.ACTIVE_ENERGY_BURNED, diff --git a/packages/health/lib/src/health_factory.dart b/packages/health/lib/src/health_factory.dart index 974db32c5..fef88fd36 100644 --- a/packages/health/lib/src/health_factory.dart +++ b/packages/health/lib/src/health_factory.dart @@ -1,6 +1,6 @@ part of health; -/// Main class for the Plugin +/// Main class for the Plugin. class HealthFactory { static const MethodChannel _channel = MethodChannel('flutter_health'); String? _deviceId; @@ -17,26 +17,26 @@ class HealthFactory { /// Determines if the data types have been granted with the specified access rights. /// - /// Returns: - /// + /// Returns: + /// /// * true - if all of the data types have been granted with the specfied access rights. /// * false - if any of the data types has not been granted with the specified access right(s) /// * null - if it can not be determined if the data types have been granted with the specified access right(s). /// /// Parameters: - /// + /// /// * [types] - List of [HealthDataType] whose permissions are to be checked. - /// * [permissions] - Optional. + /// * [permissions] - Optional. /// + If unspecified, this method checks if each HealthDataType in [types] has been granted READ access. - /// + If specified, this method checks if each [HealthDataType] in [types] has been granted with the access specified in its + /// + If specified, this method checks if each [HealthDataType] in [types] has been granted with the access specified in its /// corresponding entry in this list. The length of this list must be equal to that of [types]. - /// + /// /// Caveat: - /// + /// /// As Apple HealthKit will not disclose if READ access has been granted for a data type due to privacy concern, /// this method can only return null to represent an undertermined status, if it is called on iOS - /// with a READ or READ_WRITE access. - /// + /// with a READ or READ_WRITE access. + /// /// On Android, this function returns true or false, depending on whether the specified access right has been granted. static Future hasPermissions(List types, {List? permissions}) async { @@ -51,8 +51,7 @@ class HealthFactory { : permissions.map((permission) => permission.index).toList(); /// On Android, if BMI is requested, then also ask for weight and height - if (_platformType == PlatformType.ANDROID) - _handleBMI(mTypes, mPermissions); + if (_platformType == PlatformType.ANDROID) _handleBMI(mTypes, mPermissions); return await _channel.invokeMethod('hasPermissions', { "types": mTypes.map((type) => _enumToString(type)).toList(), @@ -71,24 +70,24 @@ class HealthFactory { }); } - /// iOS isn't supported by HealthKit, method does nothing. + /// Revoke permissions obtained earlier. + /// + /// Not supported on iOS and method does nothing. static Future revokePermissions() async { return await _channel.invokeMethod('revokePermissions'); } + /// Requests permissions to access data types in Apple Health or Google Fit. /// - /// Requests permissions to access data types in the HealthKit or Google Fit store. + /// Returns true if successful, false otherwise /// - /// Returns a Future of true if successful, a Future of false otherwise - /// - /// Parameters + /// Parameters: /// /// * [types] - a list of [HealthDataType] which the permissions are requested for. - /// * [permissions] - Optional. + /// * [permissions] - Optional. /// + If unspecified, each [HealthDataType] in [types] is requested for READ [HealthDataAccess]. - /// + If specified, each [HealthDataAccess] in this list is requested for its corresponding indexed + /// + If specified, each [HealthDataAccess] in this list is requested for its corresponding indexed /// entry in [types]. In addition, the length of this list must be equal to that of [types]. - /// Future requestAuthorization(List types, {List? permissions}) async { if (permissions != null && permissions.length != types.length) { @@ -102,9 +101,8 @@ class HealthFactory { growable: true) : permissions.map((permission) => permission.index).toList(); - /// On Android, if BMI is requested, then also ask for weight and height - if (_platformType == PlatformType.ANDROID) - _handleBMI(mTypes, mPermissions); + // on Android, if BMI is requested, then also ask for weight and height + if (_platformType == PlatformType.ANDROID) _handleBMI(mTypes, mPermissions); List keys = mTypes.map((e) => _enumToString(e)).toList(); final bool isAuthorized = await _channel.invokeMethod( @@ -112,8 +110,7 @@ class HealthFactory { return isAuthorized; } - static void _handleBMI( - List mTypes, List mPermissions) { + static void _handleBMI(List mTypes, List mPermissions) { final index = mTypes.indexOf(HealthDataType.BODY_MASS_INDEX); if (index != -1 && _platformType == PlatformType.ANDROID) { @@ -159,13 +156,11 @@ class HealthFactory { return bmiHealthPoints; } + /// Saves health data into Apple Health or Google Fit. /// - /// Saves health data into the HealthKit or Google Fit store. - /// - /// Returns a Future of true if successful, a Future of false otherwise + /// Returns true if successful, false otherwise. /// /// Parameters: - /// /// * [value] - the health data's value in double /// * [type] - the value's HealthDataType /// * [startTime] - the start time when this [value] is measured. @@ -174,8 +169,12 @@ class HealthFactory { /// + It must be equal to or later than [startTime]. /// + Simply set [endTime] equal to [startTime] if the [value] is measured only at a specific point in time. /// - Future writeHealthData(double value, HealthDataType type, - DateTime startTime, DateTime endTime) async { + Future writeHealthData( + double value, + HealthDataType type, + DateTime startTime, + DateTime endTime, + ) async { if (startTime.isAfter(endTime)) throw ArgumentError("startTime must be equal or earlier than endTime"); Map args = { @@ -188,8 +187,12 @@ class HealthFactory { return success ?? false; } + /// Fetch a list of health data points based on [types]. Future> getHealthDataFromTypes( - DateTime startDate, DateTime endDate, List types) async { + DateTime startDate, + DateTime endDate, + List types, + ) async { List dataPoints = []; for (var type in types) { @@ -224,7 +227,6 @@ class HealthFactory { /// The main function for fetching health data Future> _dataQuery( DateTime startDate, DateTime endDate, HealthDataType dataType) async { - // Set parameters for method channel request final args = { 'dataTypeKey': _enumToString(dataType), 'startDate': startDate.millisecondsSinceEpoch, @@ -278,11 +280,14 @@ class HealthFactory { return unique; } + /// Get the total numbner of steps within a specific time period. + /// Returns null if not successful. + /// + /// Is a fix according to https://stackoverflow.com/questions/29414386/step-count-retrieved-through-google-fit-api-does-not-match-step-count-displayed/29415091#29415091 Future getTotalStepsInInterval( DateTime startDate, DateTime endDate, ) async { - // Set parameters for method channel request final args = { 'startDate': startDate.millisecondsSinceEpoch, 'endDate': endDate.millisecondsSinceEpoch diff --git a/packages/health/pubspec.yaml b/packages/health/pubspec.yaml index 5ee963dce..0b85d3df6 100644 --- a/packages/health/pubspec.yaml +++ b/packages/health/pubspec.yaml @@ -1,6 +1,6 @@ name: health description: Wrapper for the iOS HealthKit and Android GoogleFit services. -version: 3.3.1 +version: 3.4.0 homepage: https://github.com/cph-cachet/flutter-plugins/tree/master/packages/health environment: From 4d446ae083c831336b1c3d33bab22dc8ac3af1ad Mon Sep 17 00:00:00 2001 From: bardram Date: Wed, 29 Dec 2021 21:33:35 +0100 Subject: [PATCH 11/30] health v 3.4.0 published --- .gitignore | 1 + packages/health/example/.flutter-plugins-dependencies | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 86e4f2f17..30fa7670d 100644 --- a/.gitignore +++ b/.gitignore @@ -123,3 +123,4 @@ packages/audio_streamer/example/.flutter-plugins-dependencies packages/health/example/.flutter-plugins-dependencies *.sh packages/health/example/.flutter-plugins-dependencies +packages/health/example/.flutter-plugins-dependencies diff --git a/packages/health/example/.flutter-plugins-dependencies b/packages/health/example/.flutter-plugins-dependencies index ad5314445..4768c46ee 100644 --- a/packages/health/example/.flutter-plugins-dependencies +++ b/packages/health/example/.flutter-plugins-dependencies @@ -1 +1 @@ -{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"device_info_plus","path":"/Users/bardram/.pub-cache/hosted/pub.dartlang.org/device_info_plus-3.1.1/","dependencies":[]},{"name":"health","path":"/Users/bardram/dev/flutter-plugins/packages/health/","dependencies":["device_info_plus"]}],"android":[{"name":"device_info_plus","path":"/Users/bardram/.pub-cache/hosted/pub.dartlang.org/device_info_plus-3.1.1/","dependencies":[]},{"name":"health","path":"/Users/bardram/dev/flutter-plugins/packages/health/","dependencies":["device_info_plus"]}],"macos":[{"name":"device_info_plus_macos","path":"/Users/bardram/.pub-cache/hosted/pub.dartlang.org/device_info_plus_macos-2.2.0/","dependencies":[]}],"linux":[],"windows":[],"web":[{"name":"device_info_plus_web","path":"/Users/bardram/.pub-cache/hosted/pub.dartlang.org/device_info_plus_web-2.1.0/","dependencies":[]}]},"dependencyGraph":[{"name":"device_info_plus","dependencies":["device_info_plus_macos","device_info_plus_web"]},{"name":"device_info_plus_macos","dependencies":[]},{"name":"device_info_plus_web","dependencies":[]},{"name":"health","dependencies":["device_info_plus"]}],"date_created":"2021-12-29 20:33:27.370175","version":"2.5.3"} \ No newline at end of file +{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"device_info_plus","path":"/Users/bardram/.pub-cache/hosted/pub.dartlang.org/device_info_plus-3.1.1/","dependencies":[]},{"name":"health","path":"/Users/bardram/dev/flutter-plugins/packages/health/","dependencies":["device_info_plus"]}],"android":[{"name":"device_info_plus","path":"/Users/bardram/.pub-cache/hosted/pub.dartlang.org/device_info_plus-3.1.1/","dependencies":[]},{"name":"health","path":"/Users/bardram/dev/flutter-plugins/packages/health/","dependencies":["device_info_plus"]}],"macos":[{"name":"device_info_plus_macos","path":"/Users/bardram/.pub-cache/hosted/pub.dartlang.org/device_info_plus_macos-2.2.0/","dependencies":[]}],"linux":[],"windows":[],"web":[{"name":"device_info_plus_web","path":"/Users/bardram/.pub-cache/hosted/pub.dartlang.org/device_info_plus_web-2.1.0/","dependencies":[]}]},"dependencyGraph":[{"name":"device_info_plus","dependencies":["device_info_plus_macos","device_info_plus_web"]},{"name":"device_info_plus_macos","dependencies":[]},{"name":"device_info_plus_web","dependencies":[]},{"name":"health","dependencies":["device_info_plus"]}],"date_created":"2021-12-29 21:18:27.488582","version":"2.5.3"} \ No newline at end of file From 2bc3cf09b3519b564319ad728ac045d21c3ad9b6 Mon Sep 17 00:00:00 2001 From: bardram Date: Thu, 30 Dec 2021 15:11:28 +0100 Subject: [PATCH 12/30] activity_recognition published v. 4.1.0 --- .gitignore | 1 + .../activity_recognition_flutter/CHANGELOG.md | 5 ++ .../ActivityRecognitionFlutterPlugin.java | 17 ++-- .../example/.flutter-plugins-dependencies | 1 - .../ios/Flutter/flutter_export_environment.sh | 14 --- .../ios/Runner.xcodeproj/project.pbxproj | 13 +-- .../xcshareddata/WorkspaceSettings.xcsettings | 2 +- .../xcshareddata/xcschemes/Runner.xcscheme | 10 +-- .../example/ios/Runner/Info.plist | 2 - .../example/lib/main.dart | 86 +++++++++++++------ .../example/pubspec.yaml | 1 + .../example/test/widget_test.dart | 2 +- .../lib/activity_recognition_flutter.dart | 2 +- .../activity_recognition_flutter/pubspec.yaml | 2 +- 14 files changed, 93 insertions(+), 65 deletions(-) delete mode 100644 packages/activity_recognition_flutter/example/.flutter-plugins-dependencies delete mode 100755 packages/activity_recognition_flutter/example/ios/Flutter/flutter_export_environment.sh diff --git a/.gitignore b/.gitignore index 30fa7670d..0de9e2302 100644 --- a/.gitignore +++ b/.gitignore @@ -124,3 +124,4 @@ packages/health/example/.flutter-plugins-dependencies *.sh packages/health/example/.flutter-plugins-dependencies packages/health/example/.flutter-plugins-dependencies +packages/activity_recognition_flutter/example/.flutter-plugins-dependencies diff --git a/packages/activity_recognition_flutter/CHANGELOG.md b/packages/activity_recognition_flutter/CHANGELOG.md index 37e1eef49..3bbbed3d4 100644 --- a/packages/activity_recognition_flutter/CHANGELOG.md +++ b/packages/activity_recognition_flutter/CHANGELOG.md @@ -1,3 +1,8 @@ +## 4.1.0 +* [PR #474](https://github.com/cph-cachet/flutter-plugins/pull/474) - Android 12 intent-flag +* then name of the stream has been changed from `startStream` to `activityStream` +* cleanup in example app + ## 4.0.5+1 * [PR #408](https://github.com/cph-cachet/flutter-plugins/pull/408) diff --git a/packages/activity_recognition_flutter/android/src/main/java/dk/cachet/activity_recognition_flutter/ActivityRecognitionFlutterPlugin.java b/packages/activity_recognition_flutter/android/src/main/java/dk/cachet/activity_recognition_flutter/ActivityRecognitionFlutterPlugin.java index 9f02e5dcb..6fb0c41d6 100644 --- a/packages/activity_recognition_flutter/android/src/main/java/dk/cachet/activity_recognition_flutter/ActivityRecognitionFlutterPlugin.java +++ b/packages/activity_recognition_flutter/android/src/main/java/dk/cachet/activity_recognition_flutter/ActivityRecognitionFlutterPlugin.java @@ -45,8 +45,10 @@ public class ActivityRecognitionFlutterPlugin implements FlutterPlugin, EventCha private void startActivityTracking() { // Start the service Intent intent = new Intent(androidActivity, ActivityRecognizedBroadcastReceiver.class); + + Log.d(TAG, "SDK = " + Build.VERSION.SDK_INT); int flags = PendingIntent.FLAG_UPDATE_CURRENT; - if (Build.VERSION.SDK_INT >= 23) { + if (Build.VERSION.SDK_INT >= 31) { flags |= PendingIntent.FLAG_IMMUTABLE; } PendingIntent pendingIntent = PendingIntent.getBroadcast(androidActivity, 0, intent, flags); @@ -59,13 +61,13 @@ private void startActivityTracking() { task.addOnSuccessListener(new OnSuccessListener() { @Override public void onSuccess(Void e) { - Log.d(TAG, "ActivityRecognition: onSuccess"); + Log.d(TAG, "Successfully registered ActivityRecognition listener."); } }); task.addOnFailureListener(new OnFailureListener() { @Override public void onFailure(@NonNull Exception e) { - Log.d(TAG, "ActivityRecognition: onFailure"); + Log.d(TAG, "Failed to registered ActivityRecognition listener."); } }); } @@ -86,12 +88,11 @@ public void onAttachedToEngine(@NonNull FlutterPluginBinding flutterPluginBindin @Override public void onListen(Object arguments, EventChannel.EventSink events) { HashMap args = (HashMap) arguments; - Log.d(TAG, "args: " + args); boolean fg = (boolean) args.get("foreground"); if(fg) { startForegroundService(); } - Log.d(TAG, "foreground: " + fg); + Log.d(TAG, "Foreground moce: " + fg); eventSink = events; @@ -136,7 +137,7 @@ public void onAttachedToActivity(@NonNull ActivityPluginBinding binding) { SharedPreferences prefs = androidContext.getSharedPreferences(ACTIVITY_RECOGNITION, Context.MODE_PRIVATE); prefs.registerOnSharedPreferenceChangeListener(this); - Log.d(TAG, "onAttachedToActivity"); + // Log.d(TAG, "onAttachedToActivity"); } @Override @@ -165,9 +166,9 @@ public void onDetachedFromActivity() { public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { String result = sharedPreferences .getString(DETECTED_ACTIVITY, "error"); - Log.d("onSharedPreferenceChange", result); + // Log.d("onSharedPreferenceChange", result); if (key!= null && key.equals(DETECTED_ACTIVITY)) { - Log.d(TAG, "Detected activity: " + result); + // Log.d(TAG, "Detected activity: " + result); eventSink.success(result); } } diff --git a/packages/activity_recognition_flutter/example/.flutter-plugins-dependencies b/packages/activity_recognition_flutter/example/.flutter-plugins-dependencies deleted file mode 100644 index f70808a88..000000000 --- a/packages/activity_recognition_flutter/example/.flutter-plugins-dependencies +++ /dev/null @@ -1 +0,0 @@ -{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"activity_recognition_flutter","path":"/Users/bardram/dev/flutter-plugins/packages/activity_recognition_flutter/","dependencies":[]},{"name":"permission_handler","path":"/Users/bardram/.pub-cache/hosted/pub.dartlang.org/permission_handler-7.1.0/","dependencies":[]}],"android":[{"name":"activity_recognition_flutter","path":"/Users/bardram/dev/flutter-plugins/packages/activity_recognition_flutter/","dependencies":[]},{"name":"permission_handler","path":"/Users/bardram/.pub-cache/hosted/pub.dartlang.org/permission_handler-7.1.0/","dependencies":[]}],"macos":[],"linux":[],"windows":[],"web":[]},"dependencyGraph":[{"name":"activity_recognition_flutter","dependencies":[]},{"name":"permission_handler","dependencies":[]}],"date_created":"2021-10-06 09:54:26.345214","version":"2.2.3"} \ No newline at end of file diff --git a/packages/activity_recognition_flutter/example/ios/Flutter/flutter_export_environment.sh b/packages/activity_recognition_flutter/example/ios/Flutter/flutter_export_environment.sh deleted file mode 100755 index 934c5797a..000000000 --- a/packages/activity_recognition_flutter/example/ios/Flutter/flutter_export_environment.sh +++ /dev/null @@ -1,14 +0,0 @@ -#!/bin/sh -# This is a generated file; do not edit or check into version control. -export "FLUTTER_ROOT=/Users/bardram/dev/flutter" -export "FLUTTER_APPLICATION_PATH=/Users/bardram/dev/flutter-plugins/packages/activity_recognition_flutter/example" -export "COCOAPODS_PARALLEL_CODE_SIGN=true" -export "FLUTTER_TARGET=lib/main.dart" -export "FLUTTER_BUILD_DIR=build" -export "SYMROOT=${SOURCE_ROOT}/../build/ios" -export "FLUTTER_BUILD_NAME=1.0.0" -export "FLUTTER_BUILD_NUMBER=1" -export "DART_OBFUSCATION=false" -export "TRACK_WIDGET_CREATION=false" -export "TREE_SHAKE_ICONS=false" -export "PACKAGE_CONFIG=.packages" diff --git a/packages/activity_recognition_flutter/example/ios/Runner.xcodeproj/project.pbxproj b/packages/activity_recognition_flutter/example/ios/Runner.xcodeproj/project.pbxproj index 12033be9e..459ed6684 100644 --- a/packages/activity_recognition_flutter/example/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/activity_recognition_flutter/example/ios/Runner.xcodeproj/project.pbxproj @@ -155,7 +155,7 @@ 97C146E61CF9000F007C117D /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 1020; + LastUpgradeCheck = 1320; ORGANIZATIONNAME = ""; TargetAttributes = { 97C146ED1CF9000F007C117D = { @@ -321,6 +321,7 @@ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; @@ -339,7 +340,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SUPPORTED_PLATFORMS = iphoneos; @@ -400,6 +401,7 @@ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; @@ -424,7 +426,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -455,6 +457,7 @@ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; @@ -473,7 +476,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SUPPORTED_PLATFORMS = iphoneos; @@ -491,7 +494,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; - DEVELOPMENT_TEAM = H33599VJ27; + DEVELOPMENT_TEAM = 8TB3T6MAZG; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", diff --git a/packages/activity_recognition_flutter/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/packages/activity_recognition_flutter/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings index 6b30c7459..530b83358 100644 --- a/packages/activity_recognition_flutter/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings +++ b/packages/activity_recognition_flutter/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -3,7 +3,7 @@ BuildSystemType - Original + Latest PreviewsEnabled diff --git a/packages/activity_recognition_flutter/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/packages/activity_recognition_flutter/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index a28140cfd..6dd6010f8 100644 --- a/packages/activity_recognition_flutter/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/packages/activity_recognition_flutter/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -1,6 +1,6 @@ - - - - + + - - UISupportedInterfaceOrientations UIInterfaceOrientationPortrait - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight UISupportedInterfaceOrientations~ipad diff --git a/packages/activity_recognition_flutter/example/lib/main.dart b/packages/activity_recognition_flutter/example/lib/main.dart index 9050184c3..d760cee6c 100644 --- a/packages/activity_recognition_flutter/example/lib/main.dart +++ b/packages/activity_recognition_flutter/example/lib/main.dart @@ -1,19 +1,19 @@ +import 'dart:async'; import 'dart:io'; import 'package:activity_recognition_flutter/activity_recognition_flutter.dart'; import 'package:flutter/material.dart'; import 'package:permission_handler/permission_handler.dart'; -void main() => runApp(new MyApp()); +void main() => runApp(new ActivityRecognitionApp()); -class MyApp extends StatefulWidget { +class ActivityRecognitionApp extends StatefulWidget { @override - _MyAppState createState() => new _MyAppState(); + _ActivityRecognitionAppState createState() => _ActivityRecognitionAppState(); } -class _MyAppState extends State { - late Stream activityStream; - ActivityEvent latestActivity = ActivityEvent.empty(); +class _ActivityRecognitionAppState extends State { + StreamSubscription? activityStreamSubscription; List _events = []; ActivityRecognition activityRecognition = ActivityRecognition.instance; @@ -21,55 +21,93 @@ class _MyAppState extends State { void initState() { super.initState(); _init(); + _events.add(ActivityEvent.empty()); + } + + @override + void dispose() { + activityStreamSubscription?.cancel(); + super.dispose(); } void _init() async { - /// Android requires explicitly asking permission + // Android requires explicitly asking permission if (Platform.isAndroid) { if (await Permission.activityRecognition.request().isGranted) { _startTracking(); } } - /// iOS does not + // iOS does not else { _startTracking(); } } void _startTracking() { - activityStream = - activityRecognition.startStream(runForegroundService: true); - activityStream.listen(onData); + activityStreamSubscription = activityRecognition + .activityStream(runForegroundService: true) + .listen(onData, onError: onError); } void onData(ActivityEvent activityEvent) { - print(activityEvent.toString()); + print('ACTIVITY - $activityEvent'); setState(() { _events.add(activityEvent); - latestActivity = activityEvent; }); } + void onError(Object error) { + print('ERROR - $error'); + } + @override Widget build(BuildContext context) { - return new MaterialApp( - home: new Scaffold( - appBar: new AppBar( - title: const Text('Activity Recognition Demo'), + return MaterialApp( + home: Scaffold( + appBar: AppBar( + title: const Text('Activity Recognition'), ), - body: new Center( - child: new ListView.builder( + body: Center( + child: ListView.builder( itemCount: _events.length, reverse: true, - itemBuilder: (BuildContext context, int idx) { - final entry = _events[idx]; + itemBuilder: (_, int idx) { + final activity = _events[idx]; return ListTile( - leading: - Text(entry.timeStamp.toString().substring(0, 19)), - trailing: Text(entry.type.toString().split('.').last)); + leading: _activityIcon(activity.type), + title: Text( + '${activity.type.toString().split('.').last} (${activity.confidence}%)'), + trailing: Text(activity.timeStamp + .toString() + .split(' ') + .last + .split('.') + .first), + ); })), ), ); } + + Icon _activityIcon(ActivityType type) { + switch (type) { + case ActivityType.WALKING: + return Icon(Icons.directions_walk); + case ActivityType.IN_VEHICLE: + return Icon(Icons.car_rental); + case ActivityType.ON_BICYCLE: + return Icon(Icons.pedal_bike); + case ActivityType.ON_FOOT: + return Icon(Icons.directions_walk); + case ActivityType.RUNNING: + return Icon(Icons.run_circle); + case ActivityType.STILL: + return Icon(Icons.cancel_outlined); + case ActivityType.TILTING: + return Icon(Icons.redo); + default: + return Icon(Icons.device_unknown); + } + } } diff --git a/packages/activity_recognition_flutter/example/pubspec.yaml b/packages/activity_recognition_flutter/example/pubspec.yaml index 46219a48b..479b39522 100644 --- a/packages/activity_recognition_flutter/example/pubspec.yaml +++ b/packages/activity_recognition_flutter/example/pubspec.yaml @@ -1,4 +1,5 @@ name: activity_recognition_flutter_example +version: 4.1.0 description: Demonstrates how to use the activity_recognition_flutter plugin. # The following line prevents the package from being accidentally published to diff --git a/packages/activity_recognition_flutter/example/test/widget_test.dart b/packages/activity_recognition_flutter/example/test/widget_test.dart index 90c68b250..6e03e9854 100644 --- a/packages/activity_recognition_flutter/example/test/widget_test.dart +++ b/packages/activity_recognition_flutter/example/test/widget_test.dart @@ -13,7 +13,7 @@ import 'package:activity_recognition_flutter_example/main.dart'; void main() { testWidgets('Verify Platform version', (WidgetTester tester) async { // Build our app and trigger a frame. - await tester.pumpWidget(MyApp()); + await tester.pumpWidget(ActivityRecognitionApp()); // Verify that platform version is retrieved. expect( diff --git a/packages/activity_recognition_flutter/lib/activity_recognition_flutter.dart b/packages/activity_recognition_flutter/lib/activity_recognition_flutter.dart index 2b7f86f69..a2c49088d 100644 --- a/packages/activity_recognition_flutter/lib/activity_recognition_flutter.dart +++ b/packages/activity_recognition_flutter/lib/activity_recognition_flutter.dart @@ -27,7 +27,7 @@ class ActivityRecognition { /// By default the foreground service is enabled, which allows the /// updates to be streamed while the app runs in the background. /// The programmer can choose to not enable to foreground service. - Stream startStream({bool runForegroundService = true}) { + Stream activityStream({bool runForegroundService = true}) { if (_stream == null) { _stream = _eventChannel .receiveBroadcastStream({"foreground": runForegroundService}).map( diff --git a/packages/activity_recognition_flutter/pubspec.yaml b/packages/activity_recognition_flutter/pubspec.yaml index e10533abb..67b4f1feb 100644 --- a/packages/activity_recognition_flutter/pubspec.yaml +++ b/packages/activity_recognition_flutter/pubspec.yaml @@ -1,6 +1,6 @@ name: activity_recognition_flutter description: Activity recognition plugin for Android and iOS. Provides event-based information about activities detected by the phone. -version: 4.0.5+1 +version: 4.1.0 homepage: https://github.com/cph-cachet/flutter-plugins/tree/master/packages environment: From e40e223fdd67628dfb7322ee273b688628e758c1 Mon Sep 17 00:00:00 2001 From: bardram Date: Fri, 31 Dec 2021 15:25:04 +0100 Subject: [PATCH 13/30] small typo fix --- .../ActivityRecognitionFlutterPlugin.java | 2 +- .../example/lib/main.dart | 35 ++++++++++--------- 2 files changed, 19 insertions(+), 18 deletions(-) diff --git a/packages/activity_recognition_flutter/android/src/main/java/dk/cachet/activity_recognition_flutter/ActivityRecognitionFlutterPlugin.java b/packages/activity_recognition_flutter/android/src/main/java/dk/cachet/activity_recognition_flutter/ActivityRecognitionFlutterPlugin.java index 6fb0c41d6..27198b955 100644 --- a/packages/activity_recognition_flutter/android/src/main/java/dk/cachet/activity_recognition_flutter/ActivityRecognitionFlutterPlugin.java +++ b/packages/activity_recognition_flutter/android/src/main/java/dk/cachet/activity_recognition_flutter/ActivityRecognitionFlutterPlugin.java @@ -92,7 +92,7 @@ public void onListen(Object arguments, EventChannel.EventSink events) { if(fg) { startForegroundService(); } - Log.d(TAG, "Foreground moce: " + fg); + Log.d(TAG, "Foreground mode: " + fg); eventSink = events; diff --git a/packages/activity_recognition_flutter/example/lib/main.dart b/packages/activity_recognition_flutter/example/lib/main.dart index d760cee6c..62e553122 100644 --- a/packages/activity_recognition_flutter/example/lib/main.dart +++ b/packages/activity_recognition_flutter/example/lib/main.dart @@ -69,23 +69,24 @@ class _ActivityRecognitionAppState extends State { title: const Text('Activity Recognition'), ), body: Center( - child: ListView.builder( - itemCount: _events.length, - reverse: true, - itemBuilder: (_, int idx) { - final activity = _events[idx]; - return ListTile( - leading: _activityIcon(activity.type), - title: Text( - '${activity.type.toString().split('.').last} (${activity.confidence}%)'), - trailing: Text(activity.timeStamp - .toString() - .split(' ') - .last - .split('.') - .first), - ); - })), + child: ListView.builder( + itemCount: _events.length, + reverse: true, + itemBuilder: (_, int idx) { + final activity = _events[idx]; + return ListTile( + leading: _activityIcon(activity.type), + title: Text( + '${activity.type.toString().split('.').last} (${activity.confidence}%)'), + trailing: Text(activity.timeStamp + .toString() + .split(' ') + .last + .split('.') + .first), + ); + }), + ), ), ); } From d7dd774c4dd929c4888ab1fc3670074eaf4cd36d Mon Sep 17 00:00:00 2001 From: bardram Date: Tue, 4 Jan 2022 00:13:06 +0100 Subject: [PATCH 14/30] Update CHANGELOG.md --- packages/activity_recognition_flutter/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/activity_recognition_flutter/CHANGELOG.md b/packages/activity_recognition_flutter/CHANGELOG.md index 3bbbed3d4..d145fdb2c 100644 --- a/packages/activity_recognition_flutter/CHANGELOG.md +++ b/packages/activity_recognition_flutter/CHANGELOG.md @@ -1,6 +1,6 @@ ## 4.1.0 * [PR #474](https://github.com/cph-cachet/flutter-plugins/pull/474) - Android 12 intent-flag -* then name of the stream has been changed from `startStream` to `activityStream` +* the name of the stream has been changed from `startStream` to `activityStream` * cleanup in example app ## 4.0.5+1 From 63efba69d567cc92880c4ce9fbfdd6b48a470bf7 Mon Sep 17 00:00:00 2001 From: Kin Mak Date: Thu, 25 Nov 2021 18:45:46 +0800 Subject: [PATCH 15/30] moved parsing out of main thread --- .../ios/Classes/SwiftHealthPlugin.swift | 69 ++++++++++--------- 1 file changed, 36 insertions(+), 33 deletions(-) diff --git a/packages/health/ios/Classes/SwiftHealthPlugin.swift b/packages/health/ios/Classes/SwiftHealthPlugin.swift index ffab6192e..07963ec9a 100644 --- a/packages/health/ios/Classes/SwiftHealthPlugin.swift +++ b/packages/health/ios/Classes/SwiftHealthPlugin.swift @@ -224,19 +224,19 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { switch samplesOrNil { case let (samples as [HKQuantitySample]) as Any: + let dictionaries = samples.map { sample -> NSDictionary in + let unit = self.unitLookUp(key: dataTypeKey) + return [ + "uuid": "\(sample.uuid)", + "value": sample.quantity.doubleValue(for: unit), + "date_from": Int(sample.startDate.timeIntervalSince1970 * 1000), + "date_to": Int(sample.endDate.timeIntervalSince1970 * 1000), + "source_id": sample.sourceRevision.source.bundleIdentifier, + "source_name": sample.sourceRevision.source.name + ] + } DispatchQueue.main.async { - result(samples.map { sample -> NSDictionary in - let unit = self.unitLookUp(key: dataTypeKey) - - return [ - "uuid": "\(sample.uuid)", - "value": sample.quantity.doubleValue(for: unit), - "date_from": Int(sample.startDate.timeIntervalSince1970 * 1000), - "date_to": Int(sample.endDate.timeIntervalSince1970 * 1000), - "source_id": sample.sourceRevision.source.bundleIdentifier, - "source_name": sample.sourceRevision.source.name - ] - }) + result(dictionaries) } case var (samplesCategory as [HKCategorySample]) as Any: @@ -249,32 +249,35 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { if (dataTypeKey == self.SLEEP_ASLEEP) { samplesCategory = samplesCategory.filter { $0.value == 1 } } - + let categories = samplesCategory.map { sample -> NSDictionary in + return [ + "uuid": "\(sample.uuid)", + "value": sample.value, + "date_from": Int(sample.startDate.timeIntervalSince1970 * 1000), + "date_to": Int(sample.endDate.timeIntervalSince1970 * 1000), + "source_id": sample.sourceRevision.source.bundleIdentifier, + "source_name": sample.sourceRevision.source.name + ] + } DispatchQueue.main.async { - result(samplesCategory.map { sample -> NSDictionary in - return [ - "uuid": "\(sample.uuid)", - "value": sample.value, - "date_from": Int(sample.startDate.timeIntervalSince1970 * 1000), - "date_to": Int(sample.endDate.timeIntervalSince1970 * 1000), - "source_id": sample.sourceRevision.source.bundleIdentifier, - "source_name": sample.sourceRevision.source.name - ] - }) + result(categories) } case let (samplesWorkout as [HKWorkout]) as Any: + + let dictionaries = samplesWorkout.map { sample -> NSDictionary in + return [ + "uuid": "\(sample.uuid)", + "value": Int(sample.duration), + "date_from": Int(sample.startDate.timeIntervalSince1970 * 1000), + "date_to": Int(sample.endDate.timeIntervalSince1970 * 1000), + "source_id": sample.sourceRevision.source.bundleIdentifier, + "source_name": sample.sourceRevision.source.name + ] + } + DispatchQueue.main.async { - result(samplesWorkout.map { sample -> NSDictionary in - return [ - "uuid": "\(sample.uuid)", - "value": Int(sample.duration), - "date_from": Int(sample.startDate.timeIntervalSince1970 * 1000), - "date_to": Int(sample.endDate.timeIntervalSince1970 * 1000), - "source_id": sample.sourceRevision.source.bundleIdentifier, - "source_name": sample.sourceRevision.source.name - ] - }) + result(dictionaries) } default: From 33842eb81bd40e1f8fdedbb52a0c6c1f6515bfb0 Mon Sep 17 00:00:00 2001 From: Kin Mak Date: Fri, 26 Nov 2021 17:40:36 +0800 Subject: [PATCH 16/30] using Isolate and moved parsing out of main thread --- packages/health/lib/health.dart | 1 + packages/health/lib/src/health_factory.dart | 65 ++++++++++++++------- 2 files changed, 45 insertions(+), 21 deletions(-) diff --git a/packages/health/lib/health.dart b/packages/health/lib/health.dart index ac6763d00..4fa0a9014 100644 --- a/packages/health/lib/health.dart +++ b/packages/health/lib/health.dart @@ -4,6 +4,7 @@ import 'dart:async'; import 'dart:io' show Platform; import 'package:device_info_plus/device_info_plus.dart'; +import 'package:flutter/foundation.dart'; import 'package:flutter/services.dart'; import 'package:intl/intl.dart'; diff --git a/packages/health/lib/src/health_factory.dart b/packages/health/lib/src/health_factory.dart index fef88fd36..8119d50ca 100644 --- a/packages/health/lib/src/health_factory.dart +++ b/packages/health/lib/src/health_factory.dart @@ -233,34 +233,57 @@ class HealthFactory { 'endDate': endDate.millisecondsSinceEpoch }; - final unit = _dataTypeToUnit[dataType]!; - final fetchedDataPoints = await _channel.invokeMethod('getData', args); if (fetchedDataPoints != null) { - return fetchedDataPoints.map((e) { - final num value = e['value']; - final DateTime from = - DateTime.fromMillisecondsSinceEpoch(e['date_from']); - final DateTime to = DateTime.fromMillisecondsSinceEpoch(e['date_to']); - final String sourceId = e["source_id"]; - final String sourceName = e["source_name"]; - return HealthDataPoint( - value, - dataType, - unit, - from, - to, - _platformType, - _deviceId!, - sourceId, - sourceName, - ); - }).toList(); + final mesg = { + "dataType": dataType, + "dataPoints": fetchedDataPoints, + "deviceId": _deviceId!, + }; + const thresHold = 10; + // If the no. of data points are larger than the threshold, + // call the compute method to spawn an Isolate to do the parsing in a separate thread. + if (fetchedDataPoints.length > thresHold) + return compute(_parse, mesg); + return _parse(mesg); } else { return []; } } + static List _parse(dynamic message) { + final dataType = message["dataType"]; + final dataPoints = message["dataPoints"]; + final device = message["deviceId"]; + final unit = _dataTypeToUnit[dataType]!; + final stopwatch = Stopwatch(); + stopwatch.start(); + + final list = dataPoints.map((e) { + final num value = e['value']; + final DateTime from = DateTime.fromMillisecondsSinceEpoch(e['date_from']); + final DateTime to = DateTime.fromMillisecondsSinceEpoch(e['date_to']); + final String sourceId = e["source_id"]; + final String sourceName = e["source_name"]; + return HealthDataPoint( + value, + dataType, + unit, + from, + to, + _platformType, + device, + sourceId, + sourceName, + ); + }).toList(); + + stopwatch.stop(); + + print('Nof items: ${list.length} time elapsed: ${stopwatch.elapsedMilliseconds} ms'); + return list; + } + /// Given an array of [HealthDataPoint]s, this method will return the array /// without any duplicates. static List removeDuplicates(List points) { From 35e547d7f226e7549316f883d9f8fa8900ec0402 Mon Sep 17 00:00:00 2001 From: Kin Mak Date: Fri, 26 Nov 2021 17:52:14 +0800 Subject: [PATCH 17/30] fixed concurrent issues by tapping from thread pool and using isolate --- .../cachet/plugins/health/HealthPlugin.kt | 354 +++++++++--------- packages/health/lib/src/health_factory.dart | 22 +- 2 files changed, 189 insertions(+), 187 deletions(-) diff --git a/packages/health/android/src/main/kotlin/cachet/plugins/health/HealthPlugin.kt b/packages/health/android/src/main/kotlin/cachet/plugins/health/HealthPlugin.kt index d3a42f142..0ac228f9a 100644 --- a/packages/health/android/src/main/kotlin/cachet/plugins/health/HealthPlugin.kt +++ b/packages/health/android/src/main/kotlin/cachet/plugins/health/HealthPlugin.kt @@ -6,7 +6,6 @@ import com.google.android.gms.fitness.Fitness import com.google.android.gms.fitness.FitnessOptions import com.google.android.gms.fitness.request.DataReadRequest import com.google.android.gms.fitness.result.DataReadResponse -import com.google.android.gms.tasks.Tasks import io.flutter.plugin.common.MethodCall import io.flutter.plugin.common.MethodChannel import io.flutter.plugin.common.MethodChannel.MethodCallHandler @@ -17,14 +16,18 @@ import android.os.Handler import android.util.Log import androidx.annotation.NonNull import io.flutter.plugin.common.PluginRegistry.ActivityResultListener -import java.util.concurrent.TimeUnit -import java.util.Date -import kotlin.concurrent.thread import com.google.android.gms.fitness.data.* import com.google.android.gms.fitness.request.SessionReadRequest +import com.google.android.gms.fitness.result.SessionReadResponse +import com.google.android.gms.tasks.OnFailureListener +import com.google.android.gms.tasks.OnSuccessListener import io.flutter.embedding.engine.plugins.FlutterPlugin import io.flutter.embedding.engine.plugins.activity.ActivityAware import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding +import java.util.concurrent.TimeUnit +import java.util.Date + +import java.util.concurrent.* const val GOOGLE_FIT_PERMISSIONS_REQUEST_CODE = 1111 @@ -35,6 +38,7 @@ class HealthPlugin(private var channel: MethodChannel? = null) : MethodCallHandl private var result: Result? = null private var handler: Handler? = null private var activity: Activity? = null + private var threadPoolExecutor: ExecutorService? = null private var BODY_FAT_PERCENTAGE = "BODY_FAT_PERCENTAGE" private var HEIGHT = "HEIGHT" @@ -58,11 +62,14 @@ class HealthPlugin(private var channel: MethodChannel? = null) : MethodCallHandl override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) { channel = MethodChannel(flutterPluginBinding.binaryMessenger, CHANNEL_NAME) channel?.setMethodCallHandler(this) + threadPoolExecutor = Executors.newFixedThreadPool(4) } override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) { channel = null activity = null + threadPoolExecutor!!.shutdown() + threadPoolExecutor = null } // This static function is optional and equivalent to onAttachedToEngine. It supports the old @@ -253,8 +260,6 @@ class HealthPlugin(private var channel: MethodChannel? = null) : MethodCallHandl typesBuilder.accessSleepSessions(FitnessOptions.ACCESS_READ) } val fitnessOptions = typesBuilder.build() - - try { val googleSignInAccount = GoogleSignIn.getAccountForExtension(activity!!.applicationContext, fitnessOptions) Fitness.getHistoryClient(activity!!.applicationContext, googleSignInAccount) @@ -272,6 +277,8 @@ class HealthPlugin(private var channel: MethodChannel? = null) : MethodCallHandl } } + + private fun getData(call: MethodCall, result: Result) { if (activity == null) { result.success(null) @@ -285,143 +292,154 @@ class HealthPlugin(private var channel: MethodChannel? = null) : MethodCallHandl // Look up data type and unit for the type key val dataType = keyToHealthDataType(type) val field = getField(type) + val typesBuilder = FitnessOptions.builder() + typesBuilder.addDataType(dataType) + if (dataType == DataType.TYPE_SLEEP_SEGMENT) { + typesBuilder.accessSleepSessions(FitnessOptions.ACCESS_READ) + } + val fitnessOptions = typesBuilder.build() + val googleSignInAccount = GoogleSignIn.getAccountForExtension(activity!!.applicationContext, fitnessOptions) - /// Start a new thread for doing a GoogleFit data lookup - thread { - try { - val typesBuilder = FitnessOptions.builder() - typesBuilder.addDataType(dataType) - if (dataType == DataType.TYPE_SLEEP_SEGMENT) { - typesBuilder.accessSleepSessions(FitnessOptions.ACCESS_READ) - } - val fitnessOptions = typesBuilder.build() - val googleSignInAccount = GoogleSignIn.getAccountForExtension(activity!!.applicationContext, fitnessOptions) - - if (dataType != DataType.TYPE_SLEEP_SEGMENT) { - val response = Fitness.getHistoryClient(activity!!.applicationContext, googleSignInAccount) - .readData(DataReadRequest.Builder() + if (dataType != DataType.TYPE_SLEEP_SEGMENT) { + Fitness.getHistoryClient(activity!!.applicationContext, googleSignInAccount) + .readData(DataReadRequest.Builder() .read(dataType) .setTimeRange(startTime, endTime, TimeUnit.MILLISECONDS) .build()) + .addOnSuccessListener (threadPoolExecutor!!, dataHandler(dataType, field, result)) + .addOnFailureListener(errHandler(result)) + } else { + // request to the sessions for sleep data + val request = SessionReadRequest.Builder() + .setTimeInterval(startTime, endTime, TimeUnit.MILLISECONDS) + .enableServerQueries() + .readSessionsFromAllApps() + .includeSleepSessions() + .build() + Fitness.getSessionsClient(activity!!.applicationContext, googleSignInAccount) + .readSession(request) + .addOnSuccessListener(threadPoolExecutor!!, sleepDataHandler(type, result)) + .addOnFailureListener(errHandler(result)) + } + + } + + private fun dataHandler(dataType: DataType, field: Field, result: Result) = + OnSuccessListener { response: DataReadResponse -> + /// Fetch all data points for the specified DataType + val dataSet = response.getDataSet(dataType) + /// For each data point, extract the contents and send them to Flutter, along with date and unit. + val healthData = dataSet.dataPoints.mapIndexed { _, dataPoint -> + return@mapIndexed hashMapOf( + "value" to getHealthDataValue(dataPoint, field), + "date_from" to dataPoint.getStartTime(TimeUnit.MILLISECONDS), + "date_to" to dataPoint.getEndTime(TimeUnit.MILLISECONDS), + "source_name" to (dataPoint.originalDataSource.appPackageName + ?: (dataPoint.originalDataSource.device?.model + ?: "")), + "source_id" to dataPoint.originalDataSource.streamIdentifier + ) + } + activity!!.runOnUiThread { result.success(healthData) } + } - /// Fetch all data points for the specified DataType - val dataPoints = Tasks.await(response).getDataSet(dataType) - - /// For each data point, extract the contents and send them to Flutter, along with date and unit. - val healthData = dataPoints.dataPoints.mapIndexed { _, dataPoint -> - return@mapIndexed hashMapOf( - "value" to getHealthDataValue(dataPoint, field), - "date_from" to dataPoint.getStartTime(TimeUnit.MILLISECONDS), - "date_to" to dataPoint.getEndTime(TimeUnit.MILLISECONDS), - "source_name" to (dataPoint.originalDataSource.appPackageName ?: (dataPoint.originalDataSource.device?.model ?: "" )), - "source_id" to dataPoint.originalDataSource.streamIdentifier + private fun errHandler(result: Result) = OnFailureListener { exception -> + activity!!.runOnUiThread { result.success(null) } + Log.i("FLUTTER_HEALTH::ERROR", exception.message ?: "unknown error") + Log.i("FLUTTER_HEALTH::ERROR", exception.stackTrace.toString()) + } + + private fun sleepDataHandler(type: String, result: Result) = + OnSuccessListener { response: SessionReadResponse -> + val healthData: MutableList> = mutableListOf() + for (session in response.sessions) { + + // Return sleep time in Minutes if requested ASLEEP data + if (type == SLEEP_ASLEEP) { + healthData.add( + hashMapOf( + "value" to session.getEndTime(TimeUnit.MINUTES) - session.getStartTime(TimeUnit.MINUTES), + "date_from" to session.getStartTime(TimeUnit.MILLISECONDS), + "date_to" to session.getEndTime(TimeUnit.MILLISECONDS), + "unit" to "MINUTES", + "source_name" to session.appPackageName, + "source_id" to session.identifier + ) ) } - activity!!.runOnUiThread { result.success(healthData) } - } else { - // request to the sessions for sleep data - val request = SessionReadRequest.Builder() - .setTimeInterval(startTime, endTime, TimeUnit.MILLISECONDS) - .enableServerQueries() - .readSessionsFromAllApps() - .includeSleepSessions() - .build() - Fitness.getSessionsClient(activity!!.applicationContext, googleSignInAccount) - .readSession(request) - .addOnSuccessListener { response -> - var healthData: MutableList> = mutableListOf() - for (session in response.sessions) { - - // Return sleep time in Minutes if requested ASLEEP data - if (type == SLEEP_ASLEEP) { + if (type == SLEEP_IN_BED) { + val dataSets = response.getDataSet(session) + + // If the sleep session has finer granularity sub-components, extract them: + if (dataSets.isNotEmpty()) { + for (dataSet in dataSets) { + for (dataPoint in dataSet.dataPoints) { + // searching OUT OF BED data + if (dataPoint.getValue(Field.FIELD_SLEEP_SEGMENT_TYPE) + .asInt() != 3 + ) { healthData.add( - hashMapOf( - "value" to session.getEndTime(TimeUnit.MINUTES) - session.getStartTime(TimeUnit.MINUTES), - "date_from" to session.getStartTime(TimeUnit.MILLISECONDS), - "date_to" to session.getEndTime(TimeUnit.MILLISECONDS), - "unit" to "MINUTES", - "source_name" to session.appPackageName, - "source_id" to session.identifier - ) - ) - } - // Returns time spent in bed in Minutes - if (type == SLEEP_IN_BED) { - val dataSets = response.getDataSet(session) - - // If the sleep session has finer granularity sub-components, extract them: - if( dataSets.isNotEmpty()){ - for (dataSet in dataSets) { - for (dataPoint in dataSet.dataPoints) { - // searching OUT OF BED data - if (dataPoint.getValue(Field.FIELD_SLEEP_SEGMENT_TYPE).asInt() != 3) { - healthData.add( - hashMapOf( - "value" to dataPoint.getEndTime(TimeUnit.MINUTES) - dataPoint.getStartTime(TimeUnit.MINUTES), - "date_from" to dataPoint.getStartTime(TimeUnit.MILLISECONDS), - "date_to" to dataPoint.getEndTime(TimeUnit.MILLISECONDS), - "unit" to "MINUTES", - "source_name" to (dataPoint.originalDataSource.appPackageName - ?: (dataPoint.originalDataSource.device?.model - ?: "unknown")), - "source_id" to dataPoint.originalDataSource.streamIdentifier - ) - ) - } - } - } - } else { - healthData.add( - hashMapOf( - "value" to session.getEndTime(TimeUnit.MINUTES) - session.getStartTime(TimeUnit.MINUTES), - "date_from" to session.getStartTime(TimeUnit.MILLISECONDS), - "date_to" to session.getEndTime(TimeUnit.MILLISECONDS), - "unit" to "MINUTES", - "source_name" to session.appPackageName, - "source_id" to session.identifier - ) + hashMapOf( + "value" to dataPoint.getEndTime(TimeUnit.MINUTES) - dataPoint.getStartTime( + TimeUnit.MINUTES + ), + "date_from" to dataPoint.getStartTime(TimeUnit.MILLISECONDS), + "date_to" to dataPoint.getEndTime(TimeUnit.MILLISECONDS), + "unit" to "MINUTES", + "source_name" to (dataPoint.originalDataSource.appPackageName + ?: (dataPoint.originalDataSource.device?.model + ?: "unknown")), + "source_id" to dataPoint.originalDataSource.streamIdentifier ) - } - } - - // If the sleep session has finer granularity sub-components, extract them: - if (type == SLEEP_AWAKE) { - val dataSets = response.getDataSet(session) - for (dataSet in dataSets) { - for (dataPoint in dataSet.dataPoints) { - // searching SLEEP AWAKE data - if (dataPoint.getValue(Field.FIELD_SLEEP_SEGMENT_TYPE).asInt() == 1) { - healthData.add( - hashMapOf( - "value" to dataPoint.getEndTime(TimeUnit.MINUTES) - dataPoint.getStartTime(TimeUnit.MINUTES), - "date_from" to dataPoint.getStartTime(TimeUnit.MILLISECONDS), - "date_to" to dataPoint.getEndTime(TimeUnit.MILLISECONDS), - "unit" to "MINUTES", - "source_name" to (dataPoint.originalDataSource.appPackageName - ?: (dataPoint.originalDataSource.device?.model - ?: "unknown")), - "source_id" to dataPoint.originalDataSource.streamIdentifier - ) - ) - } - } - } + ) } } - activity!!.runOnUiThread { result.success(healthData) } } - .addOnFailureListener { exception -> - activity!!.runOnUiThread { result.success(null) } - Log.i("FLUTTER_HEALTH::ERROR", exception.message ?: "unknown error") - Log.i("FLUTTER_HEALTH::ERROR", exception.stackTrace.toString()) + } else { + healthData.add( + hashMapOf( + "value" to session.getEndTime(TimeUnit.MINUTES) - session.getStartTime( + TimeUnit.MINUTES + ), + "date_from" to session.getStartTime(TimeUnit.MILLISECONDS), + "date_to" to session.getEndTime(TimeUnit.MILLISECONDS), + "unit" to "MINUTES", + "source_name" to session.appPackageName, + "source_id" to session.identifier + ) + ) + } + } + + if (type == SLEEP_AWAKE) { + val dataSets = response.getDataSet(session) + for (dataSet in dataSets) { + for (dataPoint in dataSet.dataPoints) { + // searching SLEEP AWAKE data + if (dataPoint.getValue(Field.FIELD_SLEEP_SEGMENT_TYPE).asInt() == 1) { + healthData.add( + hashMapOf( + "value" to dataPoint.getEndTime(TimeUnit.MINUTES) - dataPoint.getStartTime(TimeUnit.MINUTES), + "date_from" to dataPoint.getStartTime(TimeUnit.MILLISECONDS), + "date_to" to dataPoint.getEndTime(TimeUnit.MILLISECONDS), + "unit" to "MINUTES", + "source_name" to (dataPoint.originalDataSource.appPackageName + ?: (dataPoint.originalDataSource.device?.model + ?: "unknown")), + "source_id" to dataPoint.originalDataSource.streamIdentifier + ) + ) + } } + } + } } - } catch (e3: Exception) { - activity!!.runOnUiThread { result.success(null) } + activity!!.runOnUiThread { result.success(healthData) } } - } - } + + + private fun callToHealthTypes(call: MethodCall): FitnessOptions { val typesBuilder = FitnessOptions.builder() @@ -502,25 +520,10 @@ class HealthPlugin(private var channel: MethodChannel? = null) : MethodCallHandl } } - fun getTotalStepsInInterval(call: MethodCall, result: Result) { + private fun getTotalStepsInInterval(call: MethodCall, result: Result) { val start = call.argument("startDate")!! val end = call.argument("endDate")!! - getStepsInRange(start, end) { map: Map?, e: Throwable? -> - if (map != null) { - assert(map.size <= 1) { "getTotalStepsInInterval should return only one interval. Found: ${map.size}" } - result.success(map.values.firstOrNull()) - } else { - result.error("failed", e?.message, null) - } - } - } - - private fun getStepsInRange( - start: Long, - end: Long, - result: (Map?, Throwable?) -> Unit - ) { val activity = activity ?: return val stepsDataType = keyToHealthDataType(STEPS) @@ -547,44 +550,41 @@ class HealthPlugin(private var channel: MethodChannel? = null) : MethodCallHandl .setTimeRange(start, end, TimeUnit.MILLISECONDS) .build() - val response = Fitness.getHistoryClient(activity, gsa).readData(request) - - Thread { - try { - val readDataResult = Tasks.await(response) - - val map = HashMap() // need to return to Dart so can't use sparse array - for (bucket in readDataResult.buckets) { - val dp = bucket.dataSets.firstOrNull()?.dataPoints?.firstOrNull() - if (dp != null) { - print(dp) - - val count = dp.getValue(aggregatedDataType.fields[0]) - - val startTime = dp.getStartTime(TimeUnit.MILLISECONDS) - val startDate = Date(startTime) - val endDate = Date(dp.getEndTime(TimeUnit.MILLISECONDS)) - Log.i("FLUTTER_HEALTH::SUCCESS", "returning $count steps for $startDate - $endDate") - map[startTime] = count.asInt() - } else { - val startDay = Date(start) - val endDay = Date(end) - Log.i("FLUTTER_HEALTH::ERROR", "no steps for $startDay - $endDay") - } - } - activity.runOnUiThread { - result(map, null) - } - } catch (e: Throwable) { - Log.e("FLUTTER_HEALTH::ERROR", "failed: ${e.message}") + Fitness.getHistoryClient(activity, gsa).readData(request) + .addOnFailureListener(errHandler(result)) + .addOnSuccessListener(threadPoolExecutor!!, getStepsInRange(start, end, aggregatedDataType, result)) + + } + + + private fun getStepsInRange(start: Long, end: Long, aggregatedDataType: DataType , result: Result) = + OnSuccessListener { response: DataReadResponse -> - activity.runOnUiThread { - result(null, e) + val map = HashMap() // need to return to Dart so can't use sparse array + for (bucket in response.buckets) { + val dp = bucket.dataSets.firstOrNull()?.dataPoints?.firstOrNull() + if (dp != null) { + print(dp) + + val count = dp.getValue(aggregatedDataType.fields[0]) + + val startTime = dp.getStartTime(TimeUnit.MILLISECONDS) + val startDate = Date(startTime) + val endDate = Date(dp.getEndTime(TimeUnit.MILLISECONDS)) + Log.i("FLUTTER_HEALTH::SUCCESS", "returning $count steps for $startDate - $endDate") + map[startTime] = count.asInt() + } else { + val startDay = Date(start) + val endDay = Date(end) + Log.i("FLUTTER_HEALTH::ERROR", "no steps for $startDay - $endDay") } } - }.start() - } + assert(map.size <= 1) { "getTotalStepsInInterval should return only one interval. Found: ${map.size}" } + activity!!.runOnUiThread { + result.success(map.values.firstOrNull()) + } + } /// Handle calls from the MethodChannel override fun onMethodCall(call: MethodCall, result: Result) { diff --git a/packages/health/lib/src/health_factory.dart b/packages/health/lib/src/health_factory.dart index 8119d50ca..1127ad6a0 100644 --- a/packages/health/lib/src/health_factory.dart +++ b/packages/health/lib/src/health_factory.dart @@ -199,6 +199,12 @@ class HealthFactory { final result = await _prepareQuery(startDate, endDate, type); dataPoints.addAll(result); } + + const int threshold = 100; + if (dataPoints.length > threshold) { + return compute(removeDuplicates, dataPoints); + } + return removeDuplicates(dataPoints); } @@ -240,25 +246,23 @@ class HealthFactory { "dataPoints": fetchedDataPoints, "deviceId": _deviceId!, }; - const thresHold = 10; + const thresHold = 100; // If the no. of data points are larger than the threshold, // call the compute method to spawn an Isolate to do the parsing in a separate thread. - if (fetchedDataPoints.length > thresHold) + if (fetchedDataPoints.length > thresHold) { return compute(_parse, mesg); + } return _parse(mesg); } else { return []; } } - static List _parse(dynamic message) { + static List _parse(Map message) { final dataType = message["dataType"]; final dataPoints = message["dataPoints"]; final device = message["deviceId"]; - final unit = _dataTypeToUnit[dataType]!; - final stopwatch = Stopwatch(); - stopwatch.start(); - + final unit = _dataTypeToUnit[dataType]!; final list = dataPoints.map((e) { final num value = e['value']; final DateTime from = DateTime.fromMillisecondsSinceEpoch(e['date_from']); @@ -278,9 +282,6 @@ class HealthFactory { ); }).toList(); - stopwatch.stop(); - - print('Nof items: ${list.length} time elapsed: ${stopwatch.elapsedMilliseconds} ms'); return list; } @@ -294,6 +295,7 @@ class HealthFactory { for (var s in unique) { if (s == p) { seenBefore = true; + break; } } if (!seenBefore) { From 16255f72fbaf68a76c2f056ef80c2d1121038a46 Mon Sep 17 00:00:00 2001 From: Kin Mak Date: Mon, 3 Jan 2022 23:25:53 +0800 Subject: [PATCH 18/30] fixed crash by returning result to main thread --- packages/health/ios/Classes/SwiftHealthPlugin.swift | 10 ++++++++-- packages/health/lib/src/health_factory.dart | 8 ++++---- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/packages/health/ios/Classes/SwiftHealthPlugin.swift b/packages/health/ios/Classes/SwiftHealthPlugin.swift index 07963ec9a..d0c5ce7ae 100644 --- a/packages/health/ios/Classes/SwiftHealthPlugin.swift +++ b/packages/health/ios/Classes/SwiftHealthPlugin.swift @@ -308,7 +308,11 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { guard let queryResult = queryResult else { let error = error! as NSError - result(FlutterError(code: "\(error.code)", message: error.domain, details: error.localizedDescription)) + print("Error getting total steps in interval \(error.localizedDescription)") + + DispatchQueue.main.async { + result(nil) + } return } @@ -320,7 +324,9 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { } let totalSteps = Int(steps) - result(totalSteps) + DispatchQueue.main.async { + result(totalSteps) + } } HKHealthStore().execute(query) diff --git a/packages/health/lib/src/health_factory.dart b/packages/health/lib/src/health_factory.dart index 1127ad6a0..acd1d7336 100644 --- a/packages/health/lib/src/health_factory.dart +++ b/packages/health/lib/src/health_factory.dart @@ -105,9 +105,9 @@ class HealthFactory { if (_platformType == PlatformType.ANDROID) _handleBMI(mTypes, mPermissions); List keys = mTypes.map((e) => _enumToString(e)).toList(); - final bool isAuthorized = await _channel.invokeMethod( + final bool? isAuthorized = await _channel.invokeMethod( 'requestAuthorization', {'types': keys, "permissions": mPermissions}); - return isAuthorized; + return isAuthorized ?? false; } static void _handleBMI(List mTypes, List mPermissions) { @@ -262,7 +262,7 @@ class HealthFactory { final dataType = message["dataType"]; final dataPoints = message["dataPoints"]; final device = message["deviceId"]; - final unit = _dataTypeToUnit[dataType]!; + final unit = _dataTypeToUnit[dataType]!; final list = dataPoints.map((e) { final num value = e['value']; final DateTime from = DateTime.fromMillisecondsSinceEpoch(e['date_from']); @@ -317,7 +317,7 @@ class HealthFactory { 'startDate': startDate.millisecondsSinceEpoch, 'endDate': endDate.millisecondsSinceEpoch }; - final stepsCount = await _channel.invokeMethod( + final stepsCount = await _channel.invokeMethod( 'getTotalStepsInInterval', args, ); From b23d4a19026f38a71991a740859ae0ab4913aa61 Mon Sep 17 00:00:00 2001 From: Daniel Rodenberg Date: Thu, 6 Jan 2022 11:09:17 -0600 Subject: [PATCH 19/30] Healthkit CategorySample When Healthkit needs a CatergorySample the code would crash. Now checks if the unit is nothing which means that it doesn't need a quantity for the session. --- packages/health/ios/Classes/SwiftHealthPlugin.swift | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/packages/health/ios/Classes/SwiftHealthPlugin.swift b/packages/health/ios/Classes/SwiftHealthPlugin.swift index ffab6192e..8e754de7d 100644 --- a/packages/health/ios/Classes/SwiftHealthPlugin.swift +++ b/packages/health/ios/Classes/SwiftHealthPlugin.swift @@ -189,9 +189,15 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { print("Successfully called writeData with value of \(value) and type of \(type)") - let quantity = HKQuantity(unit: unitLookUp(key: type), doubleValue: value) - - let sample = HKQuantitySample(type: dataTypeLookUp(key: type) as! HKQuantityType, quantity: quantity, start: dateFrom, end: dateTo) + let sample: HKObject + + if (unitLookUp(key: type) == HKUnit.init(from: "")) { + sample = HKCategorySample(type: dataTypeLookUp(key: type) as! HKCategoryType, value: Int(value), start: dateFrom, end: dateTo) + } else { + let quantity = HKQuantity(unit: unitLookUp(key: type), doubleValue: value) + + sample = HKQuantitySample(type: dataTypeLookUp(key: type) as! HKQuantityType, quantity: quantity, start: dateFrom, end: dateTo) + } HKHealthStore().save(sample, withCompletion: { (success, error) in if let err = error { From eff4192bb03f4599dc221c71efdba6389f285a16 Mon Sep 17 00:00:00 2001 From: bardram Date: Thu, 6 Jan 2022 23:24:27 +0100 Subject: [PATCH 20/30] refactor of activity recognition package and release --- .../activity_recognition_flutter/CHANGELOG.md | 5 ++- .../android/build.gradle | 6 +-- .../ActivityRecognitionFlutterPlugin.java | 1 - .../example/android/app/build.gradle | 2 +- .../example/lib/main.dart | 6 +-- ....dart => activity_recognition_domain.dart} | 45 +++++++++---------- .../lib/activity_recognition_flutter.dart | 18 ++++---- .../activity_recognition_flutter/pubspec.yaml | 2 +- 8 files changed, 40 insertions(+), 45 deletions(-) rename packages/activity_recognition_flutter/lib/{ar_domain.dart => activity_recognition_domain.dart} (66%) diff --git a/packages/activity_recognition_flutter/CHANGELOG.md b/packages/activity_recognition_flutter/CHANGELOG.md index d145fdb2c..df13d9fb4 100644 --- a/packages/activity_recognition_flutter/CHANGELOG.md +++ b/packages/activity_recognition_flutter/CHANGELOG.md @@ -1,3 +1,7 @@ +## 4.2.0 +* small refactor and improvement of docs +* using `ActivityRecognition()` when creating a singleton -- standard practice in Dart. + ## 4.1.0 * [PR #474](https://github.com/cph-cachet/flutter-plugins/pull/474) - Android 12 intent-flag * the name of the stream has been changed from `startStream` to `activityStream` @@ -22,7 +26,6 @@ * [PR #314](https://github.com/cph-cachet/flutter-plugins/pull/314) ## 4.0.0 - - Null safety migration - Updated swift code diff --git a/packages/activity_recognition_flutter/android/build.gradle b/packages/activity_recognition_flutter/android/build.gradle index 30a2256d5..0a1316904 100644 --- a/packages/activity_recognition_flutter/android/build.gradle +++ b/packages/activity_recognition_flutter/android/build.gradle @@ -22,10 +22,10 @@ rootProject.allprojects { apply plugin: 'com.android.library' android { - compileSdkVersion 29 + compileSdkVersion 30 defaultConfig { - minSdkVersion 16 + minSdkVersion 21 } lintOptions { disable 'InvalidPackage' @@ -33,5 +33,5 @@ android { } dependencies { - implementation 'com.google.android.gms:play-services-location:16.0.0' + implementation 'com.google.android.gms:play-services-location:19.0.1' } diff --git a/packages/activity_recognition_flutter/android/src/main/java/dk/cachet/activity_recognition_flutter/ActivityRecognitionFlutterPlugin.java b/packages/activity_recognition_flutter/android/src/main/java/dk/cachet/activity_recognition_flutter/ActivityRecognitionFlutterPlugin.java index 27198b955..6df5f1cd3 100644 --- a/packages/activity_recognition_flutter/android/src/main/java/dk/cachet/activity_recognition_flutter/ActivityRecognitionFlutterPlugin.java +++ b/packages/activity_recognition_flutter/android/src/main/java/dk/cachet/activity_recognition_flutter/ActivityRecognitionFlutterPlugin.java @@ -94,7 +94,6 @@ public void onListen(Object arguments, EventChannel.EventSink events) { } Log.d(TAG, "Foreground mode: " + fg); - eventSink = events; startActivityTracking(); } diff --git a/packages/activity_recognition_flutter/example/android/app/build.gradle b/packages/activity_recognition_flutter/example/android/app/build.gradle index 94c69ba23..67e070f7f 100644 --- a/packages/activity_recognition_flutter/example/android/app/build.gradle +++ b/packages/activity_recognition_flutter/example/android/app/build.gradle @@ -34,7 +34,7 @@ android { defaultConfig { // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). applicationId "dk.cachet.activity_recognition_flutter_example" - minSdkVersion 16 + minSdkVersion 21 targetSdkVersion 30 versionCode flutterVersionCode.toInteger() versionName flutterVersionName diff --git a/packages/activity_recognition_flutter/example/lib/main.dart b/packages/activity_recognition_flutter/example/lib/main.dart index 62e553122..32680bda6 100644 --- a/packages/activity_recognition_flutter/example/lib/main.dart +++ b/packages/activity_recognition_flutter/example/lib/main.dart @@ -15,13 +15,13 @@ class ActivityRecognitionApp extends StatefulWidget { class _ActivityRecognitionAppState extends State { StreamSubscription? activityStreamSubscription; List _events = []; - ActivityRecognition activityRecognition = ActivityRecognition.instance; + ActivityRecognition activityRecognition = ActivityRecognition(); @override void initState() { super.initState(); _init(); - _events.add(ActivityEvent.empty()); + _events.add(ActivityEvent.unknown()); } @override @@ -51,7 +51,7 @@ class _ActivityRecognitionAppState extends State { } void onData(ActivityEvent activityEvent) { - print('ACTIVITY - $activityEvent'); + print(activityEvent); setState(() { _events.add(activityEvent); }); diff --git a/packages/activity_recognition_flutter/lib/ar_domain.dart b/packages/activity_recognition_flutter/lib/activity_recognition_domain.dart similarity index 66% rename from packages/activity_recognition_flutter/lib/ar_domain.dart rename to packages/activity_recognition_flutter/lib/activity_recognition_domain.dart index 02d4216e9..db65d7620 100644 --- a/packages/activity_recognition_flutter/lib/ar_domain.dart +++ b/packages/activity_recognition_flutter/lib/activity_recognition_domain.dart @@ -37,21 +37,28 @@ Map _activityMap = { /// Represents an activity event detected on the phone. class ActivityEvent { - ActivityType _type; - int _confidence; - late DateTime _timeStamp; + /// The type of activity. + ActivityType type; + + /// The confidence of the dection in percentage. + int confidence; - ActivityEvent._(this._type, this._confidence) { - this._timeStamp = DateTime.now(); + /// The timestamp when detected. + late DateTime timeStamp; + + /// The type of activity as a String. + String get typeString => type.toString().split('.').last; + + ActivityEvent(this.type, this.confidence) { + this.timeStamp = DateTime.now(); } - factory ActivityEvent.empty() => ActivityEvent._(ActivityType.UNKNOWN, 100); + factory ActivityEvent.unknown() => ActivityEvent(ActivityType.UNKNOWN, 100); - factory ActivityEvent.fromJson(String json) { - List tokens = json.split(","); - if (tokens.length < 2) { - return ActivityEvent.empty(); - } + /// Create an [ActivityEvent] based on the string format `type,confidence`. + factory ActivityEvent.fromString(String string) { + List tokens = string.split(","); + if (tokens.length < 2) return ActivityEvent.unknown(); ActivityType type = ActivityType.UNKNOWN; if (_activityMap.containsKey(tokens.first)) { @@ -59,21 +66,9 @@ class ActivityEvent { } int conf = int.tryParse(tokens.last)!; - return ActivityEvent._(type, conf); + return ActivityEvent(type, conf); } @override - String toString() { - String typeString = type.toString().split('.').last; - return 'Activity: $typeString, confidence: $confidence%'; - } - - /// The type of activity. - ActivityType get type => _type; - - /// The timestamp when detected. - DateTime get timeStamp => _timeStamp; - - /// The confidence of the dection in percentage. - int get confidence => _confidence; + String toString() => 'Activity - type: $typeString, confidence: $confidence%'; } diff --git a/packages/activity_recognition_flutter/lib/activity_recognition_flutter.dart b/packages/activity_recognition_flutter/lib/activity_recognition_flutter.dart index a2c49088d..4158d59c9 100644 --- a/packages/activity_recognition_flutter/lib/activity_recognition_flutter.dart +++ b/packages/activity_recognition_flutter/lib/activity_recognition_flutter.dart @@ -3,23 +3,21 @@ library activity_recognition; import 'dart:async'; import 'package:flutter/services.dart'; -part 'ar_domain.dart'; +part 'activity_recognition_domain.dart'; /// Main entry to activity recognition API. Use as a singleton like /// -/// `ActivityRecognition.instance` +/// `ActivityRecognition()` /// class ActivityRecognition { + static const EventChannel _eventChannel = + const EventChannel('activity_recognition_flutter'); Stream? _stream; - + static ActivityRecognition _instance = ActivityRecognition._(); ActivityRecognition._(); - static final ActivityRecognition _instance = ActivityRecognition._(); - - static ActivityRecognition get instance => _instance; - - static const EventChannel _eventChannel = - const EventChannel('activity_recognition_flutter'); + /// Get the [ActivityRecognition] singleton. + factory ActivityRecognition() => _instance; /// Requests continuous [ActivityEvent] updates. /// @@ -31,7 +29,7 @@ class ActivityRecognition { if (_stream == null) { _stream = _eventChannel .receiveBroadcastStream({"foreground": runForegroundService}).map( - (json) => ActivityEvent.fromJson(json)); + (json) => ActivityEvent.fromString(json)); } return _stream!; } diff --git a/packages/activity_recognition_flutter/pubspec.yaml b/packages/activity_recognition_flutter/pubspec.yaml index 67b4f1feb..196524ac0 100644 --- a/packages/activity_recognition_flutter/pubspec.yaml +++ b/packages/activity_recognition_flutter/pubspec.yaml @@ -1,6 +1,6 @@ name: activity_recognition_flutter description: Activity recognition plugin for Android and iOS. Provides event-based information about activities detected by the phone. -version: 4.1.0 +version: 4.2.0 homepage: https://github.com/cph-cachet/flutter-plugins/tree/master/packages environment: From 77854ec25e6b11275b6525f398b250bc762ae255 Mon Sep 17 00:00:00 2001 From: bardram Date: Sun, 9 Jan 2022 19:43:21 +0100 Subject: [PATCH 21/30] cleanup in mobility_features --- .gitignore | 2 + .../example/android/app/build.gradle | 4 +- .../android/app/src/main/AndroidManifest.xml | 9 +- .../.flutter-plugins-dependencies | 1 - packages/mobility_features/CHANGELOG.md | 4 + packages/mobility_features/README.md | 91 +-- .../example/.flutter-plugins-dependencies | 2 +- packages/mobility_features/example/README.md | 17 +- .../example/android/app/build.gradle | 4 +- .../android/app/src/main/AndroidManifest.xml | 12 +- .../mobility_features/example/lib/main.dart | 63 +- .../mobility_features/example/pubspec.yaml | 67 +- .../lib/mobility_features.dart | 2 +- .../lib/src/mobility_context.dart | 157 ++--- .../lib/src/mobility_domain.dart | 1 - ...ty_factory.dart => mobility_features.dart} | 0 .../.flutter-plugins-dependencies | 1 - .../mobility_features_example/.metadata | 10 - .../mobility_features_example/README.md | 16 - .../android/app/build.gradle | 63 -- .../android/app/src/debug/AndroidManifest.xml | 7 - .../android/app/src/main/AndroidManifest.xml | 76 --- .../mobility_features_example/MainActivity.kt | 6 - .../main/res/drawable/launch_background.xml | 12 - .../src/main/res/mipmap-hdpi/ic_launcher.png | Bin 544 -> 0 bytes .../src/main/res/mipmap-mdpi/ic_launcher.png | Bin 442 -> 0 bytes .../src/main/res/mipmap-xhdpi/ic_launcher.png | Bin 721 -> 0 bytes .../main/res/mipmap-xxhdpi/ic_launcher.png | Bin 1031 -> 0 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.png | Bin 1443 -> 0 bytes .../app/src/main/res/values/styles.xml | 18 - .../app/src/profile/AndroidManifest.xml | 7 - .../android/build.gradle | 31 - .../android/gradle.properties | 4 - .../gradle/wrapper/gradle-wrapper.properties | 6 - .../android/settings.gradle | 15 - .../ios/Flutter/AppFrameworkInfo.plist | 26 - .../ios/Flutter/Debug.xcconfig | 2 - .../ios/Flutter/Release.xcconfig | 2 - .../ios/Runner.xcodeproj/project.pbxproj | 586 ------------------ .../contents.xcworkspacedata | 7 - .../xcshareddata/IDEWorkspaceChecks.plist | 8 - .../xcshareddata/WorkspaceSettings.xcsettings | 8 - .../xcshareddata/xcschemes/Runner.xcscheme | 91 --- .../contents.xcworkspacedata | 10 - .../xcshareddata/IDEWorkspaceChecks.plist | 8 - .../xcshareddata/WorkspaceSettings.xcsettings | 8 - .../ios/Runner/AppDelegate.swift | 23 - .../AppIcon.appiconset/Contents.json | 122 ---- .../Icon-App-1024x1024@1x.png | Bin 10932 -> 0 bytes .../AppIcon.appiconset/Icon-App-20x20@1x.png | Bin 564 -> 0 bytes .../AppIcon.appiconset/Icon-App-20x20@2x.png | Bin 1283 -> 0 bytes .../AppIcon.appiconset/Icon-App-20x20@3x.png | Bin 1588 -> 0 bytes .../AppIcon.appiconset/Icon-App-29x29@1x.png | Bin 1025 -> 0 bytes .../AppIcon.appiconset/Icon-App-29x29@2x.png | Bin 1716 -> 0 bytes .../AppIcon.appiconset/Icon-App-29x29@3x.png | Bin 1920 -> 0 bytes .../AppIcon.appiconset/Icon-App-40x40@1x.png | Bin 1283 -> 0 bytes .../AppIcon.appiconset/Icon-App-40x40@2x.png | Bin 1895 -> 0 bytes .../AppIcon.appiconset/Icon-App-40x40@3x.png | Bin 2665 -> 0 bytes .../AppIcon.appiconset/Icon-App-60x60@2x.png | Bin 2665 -> 0 bytes .../AppIcon.appiconset/Icon-App-60x60@3x.png | Bin 3831 -> 0 bytes .../AppIcon.appiconset/Icon-App-76x76@1x.png | Bin 1888 -> 0 bytes .../AppIcon.appiconset/Icon-App-76x76@2x.png | Bin 3294 -> 0 bytes .../Icon-App-83.5x83.5@2x.png | Bin 3612 -> 0 bytes .../LaunchImage.imageset/Contents.json | 23 - .../LaunchImage.imageset/LaunchImage.png | Bin 68 -> 0 bytes .../LaunchImage.imageset/LaunchImage@2x.png | Bin 68 -> 0 bytes .../LaunchImage.imageset/LaunchImage@3x.png | Bin 68 -> 0 bytes .../LaunchImage.imageset/README.md | 5 - .../Runner/Base.lproj/LaunchScreen.storyboard | 37 -- .../ios/Runner/Base.lproj/Main.storyboard | 26 - .../ios/Runner/Info.plist | 55 -- .../ios/Runner/Runner-Bridging-Header.h | 1 - .../mobility_features_example/lib/main.dart | 273 -------- .../lib/moves_page.dart | 31 - .../lib/places_page.dart | 34 - .../lib/stops_page.dart | 33 - .../mobility_features_example/pubspec.yaml | 80 --- packages/mobility_features/pubspec.yaml | 2 +- .../test/mobility_features_test.dart | 4 +- .../test/testdata/location_samples.json | 20 + 80 files changed, 211 insertions(+), 2022 deletions(-) delete mode 100644 packages/mobility_features/.flutter-plugins-dependencies rename packages/mobility_features/lib/src/{mobility_factory.dart => mobility_features.dart} (100%) delete mode 100644 packages/mobility_features/mobility_features_example/.flutter-plugins-dependencies delete mode 100644 packages/mobility_features/mobility_features_example/.metadata delete mode 100644 packages/mobility_features/mobility_features_example/README.md delete mode 100644 packages/mobility_features/mobility_features_example/android/app/build.gradle delete mode 100644 packages/mobility_features/mobility_features_example/android/app/src/debug/AndroidManifest.xml delete mode 100644 packages/mobility_features/mobility_features_example/android/app/src/main/AndroidManifest.xml delete mode 100644 packages/mobility_features/mobility_features_example/android/app/src/main/kotlin/com/example/mobility_features_example/MainActivity.kt delete mode 100644 packages/mobility_features/mobility_features_example/android/app/src/main/res/drawable/launch_background.xml delete mode 100644 packages/mobility_features/mobility_features_example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png delete mode 100644 packages/mobility_features/mobility_features_example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png delete mode 100644 packages/mobility_features/mobility_features_example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png delete mode 100644 packages/mobility_features/mobility_features_example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png delete mode 100644 packages/mobility_features/mobility_features_example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png delete mode 100644 packages/mobility_features/mobility_features_example/android/app/src/main/res/values/styles.xml delete mode 100644 packages/mobility_features/mobility_features_example/android/app/src/profile/AndroidManifest.xml delete mode 100644 packages/mobility_features/mobility_features_example/android/build.gradle delete mode 100644 packages/mobility_features/mobility_features_example/android/gradle.properties delete mode 100644 packages/mobility_features/mobility_features_example/android/gradle/wrapper/gradle-wrapper.properties delete mode 100644 packages/mobility_features/mobility_features_example/android/settings.gradle delete mode 100644 packages/mobility_features/mobility_features_example/ios/Flutter/AppFrameworkInfo.plist delete mode 100644 packages/mobility_features/mobility_features_example/ios/Flutter/Debug.xcconfig delete mode 100644 packages/mobility_features/mobility_features_example/ios/Flutter/Release.xcconfig delete mode 100644 packages/mobility_features/mobility_features_example/ios/Runner.xcodeproj/project.pbxproj delete mode 100644 packages/mobility_features/mobility_features_example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata delete mode 100644 packages/mobility_features/mobility_features_example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist delete mode 100644 packages/mobility_features/mobility_features_example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings delete mode 100644 packages/mobility_features/mobility_features_example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme delete mode 100644 packages/mobility_features/mobility_features_example/ios/Runner.xcworkspace/contents.xcworkspacedata delete mode 100644 packages/mobility_features/mobility_features_example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist delete mode 100644 packages/mobility_features/mobility_features_example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings delete mode 100644 packages/mobility_features/mobility_features_example/ios/Runner/AppDelegate.swift delete mode 100644 packages/mobility_features/mobility_features_example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json delete mode 100644 packages/mobility_features/mobility_features_example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png delete mode 100644 packages/mobility_features/mobility_features_example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png delete mode 100644 packages/mobility_features/mobility_features_example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png delete mode 100644 packages/mobility_features/mobility_features_example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png delete mode 100644 packages/mobility_features/mobility_features_example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png delete mode 100644 packages/mobility_features/mobility_features_example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png delete mode 100644 packages/mobility_features/mobility_features_example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png delete mode 100644 packages/mobility_features/mobility_features_example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png delete mode 100644 packages/mobility_features/mobility_features_example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png delete mode 100644 packages/mobility_features/mobility_features_example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png delete mode 100644 packages/mobility_features/mobility_features_example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png delete mode 100644 packages/mobility_features/mobility_features_example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png delete mode 100644 packages/mobility_features/mobility_features_example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png delete mode 100644 packages/mobility_features/mobility_features_example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png delete mode 100644 packages/mobility_features/mobility_features_example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png delete mode 100644 packages/mobility_features/mobility_features_example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json delete mode 100644 packages/mobility_features/mobility_features_example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png delete mode 100644 packages/mobility_features/mobility_features_example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png delete mode 100644 packages/mobility_features/mobility_features_example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png delete mode 100644 packages/mobility_features/mobility_features_example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md delete mode 100644 packages/mobility_features/mobility_features_example/ios/Runner/Base.lproj/LaunchScreen.storyboard delete mode 100644 packages/mobility_features/mobility_features_example/ios/Runner/Base.lproj/Main.storyboard delete mode 100644 packages/mobility_features/mobility_features_example/ios/Runner/Info.plist delete mode 100644 packages/mobility_features/mobility_features_example/ios/Runner/Runner-Bridging-Header.h delete mode 100644 packages/mobility_features/mobility_features_example/lib/main.dart delete mode 100644 packages/mobility_features/mobility_features_example/lib/moves_page.dart delete mode 100644 packages/mobility_features/mobility_features_example/lib/places_page.dart delete mode 100644 packages/mobility_features/mobility_features_example/lib/stops_page.dart delete mode 100644 packages/mobility_features/mobility_features_example/pubspec.yaml diff --git a/.gitignore b/.gitignore index 0de9e2302..ed6117258 100644 --- a/.gitignore +++ b/.gitignore @@ -125,3 +125,5 @@ packages/health/example/.flutter-plugins-dependencies packages/health/example/.flutter-plugins-dependencies packages/health/example/.flutter-plugins-dependencies packages/activity_recognition_flutter/example/.flutter-plugins-dependencies +packages/mobility_features/example/.flutter-plugins-dependencies +packages/mobility_features/example/.flutter-plugins-dependencies diff --git a/packages/carp_background_location/example/android/app/build.gradle b/packages/carp_background_location/example/android/app/build.gradle index 320be2219..dcb69350b 100644 --- a/packages/carp_background_location/example/android/app/build.gradle +++ b/packages/carp_background_location/example/android/app/build.gradle @@ -26,7 +26,7 @@ apply plugin: 'kotlin-android' apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" android { - compileSdkVersion 28 + compileSdkVersion 30 sourceSets { main.java.srcDirs += 'src/main/kotlin' @@ -40,7 +40,7 @@ android { // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). applicationId "com.example.example" minSdkVersion 16 - targetSdkVersion 28 + targetSdkVersion 30 versionCode flutterVersionCode.toInteger() versionName flutterVersionName } diff --git a/packages/carp_background_location/example/android/app/src/main/AndroidManifest.xml b/packages/carp_background_location/example/android/app/src/main/AndroidManifest.xml index 4ee714d9b..4a8a4927e 100644 --- a/packages/carp_background_location/example/android/app/src/main/AndroidManifest.xml +++ b/packages/carp_background_location/example/android/app/src/main/AndroidManifest.xml @@ -16,7 +16,7 @@ - + + + + diff --git a/packages/mobility_features/.flutter-plugins-dependencies b/packages/mobility_features/.flutter-plugins-dependencies deleted file mode 100644 index 10fdb49b5..000000000 --- a/packages/mobility_features/.flutter-plugins-dependencies +++ /dev/null @@ -1 +0,0 @@ -{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"path_provider","path":"/Users/tnni/.pub-cache/hosted/pub.dartlang.org/path_provider-1.6.11/","dependencies":[]}],"android":[{"name":"path_provider","path":"/Users/tnni/.pub-cache/hosted/pub.dartlang.org/path_provider-1.6.11/","dependencies":[]}],"macos":[{"name":"path_provider_macos","path":"/Users/tnni/.pub-cache/hosted/pub.dartlang.org/path_provider_macos-0.0.4+3/","dependencies":[]}],"linux":[{"name":"path_provider_linux","path":"/Users/tnni/.pub-cache/hosted/pub.dartlang.org/path_provider_linux-0.0.1+1/","dependencies":[]}],"windows":[],"web":[]},"dependencyGraph":[{"name":"path_provider","dependencies":["path_provider_macos","path_provider_linux"]},{"name":"path_provider_linux","dependencies":[]},{"name":"path_provider_macos","dependencies":[]}],"date_created":"2020-08-14 11:55:36.022296","version":"1.17.5"} \ No newline at end of file diff --git a/packages/mobility_features/CHANGELOG.md b/packages/mobility_features/CHANGELOG.md index 59ab0c864..467662852 100644 --- a/packages/mobility_features/CHANGELOG.md +++ b/packages/mobility_features/CHANGELOG.md @@ -1,3 +1,7 @@ +## 3.1.0 +* improvement to `MobilityContext` API. +* misc updates to documentation. + ## 3.0.0+2 * update to null-safety * rename of `MobilityFactory` to `MobilityFeatures`and using the standard Dart singleton syntax for `MobilityFeatures()`. diff --git a/packages/mobility_features/README.md b/packages/mobility_features/README.md index f6dca7fbc..e7cfeae9f 100644 --- a/packages/mobility_features/README.md +++ b/packages/mobility_features/README.md @@ -2,37 +2,29 @@ ## Setup -Add the package to your `pubspec.yaml` file and import the package +The Mobility Features package is designed to work independent of the location plugin. You may choose you own location plugin, since you may already use this in your app. -No permissions are required to use the package, however, a location plugin should be used to stream data. +In the example app we use our own plugin [`carp_background_location`](https://pub.dev/packages/carp_background_location) which works on both Android and iOS as of August 2020. However, the +[location](https://pub.dev/packages/location) plugin will also work. The important thing, however, is to make sure that the app runs in the backgound. On Android this is tied to running the app as a foregound service. -We recommend our own plugin [`carp_background_location`](https://pub.dev/packages/carp_background_location) which works on both Android and iOS as of August 2020. +Add the package to your `pubspec.yaml` file and import the package ```dart import 'package:mobility_features/mobility_features.dart'; ``` -### Step 1: Choose parameters -```dart -MobilityFeatures().stopDuration = Duration(seconds: 20); -MobilityFeatures().placeRadius = 50.0; -MobilityFeatures().stopRadius = 5.0; -``` +The plugin works as a singleton and can be accessed using `MobilityFeatures()` in the code. + + +### Step 1 - Configuration of parameters -Optionally, the following configurations can be made, which will influence the algorithms for producing features: +The following configurations can be made, which will influence the algorithms for producing features: * The stop radius should be kept low (5-20 meters) * The place radius somewhat higher (25-50 meters). * The stop duration can also be set to any desired duration, for most cases it should be kept lower than 3 minutes. -Features computation is triggered when the user moves around and change their geo-position by a certain distance (stop distance). -If the stop was long enough (stop duration) the stop will be saved. Places are computed by grouping stops based on distance between them (place radius) - -Common for these parameters is that their value depend on what you are trying to capture: -Low parameter values will make the features more fine-grained but will trigger computation more often and will likely also lead to noisy features. - -For example, given a low stop duration, stopping for a red light in traffic will count as a stop. -Such granularity will be irrelevant for many use cases, but may be useful if questions such as 'do they take the same route to work every day?' are to be answered. +Configuration is done like shown below: ```dart StreamSubscription mobilitySubscription; @@ -40,44 +32,63 @@ MobilityContext _mobilityContext; void initState() { ... - MobilityFeatures().stopDuration = Duration(seconds: 30); - MobilityFeatures().placeRadius = 20; - MobilityFeatures().stopRadius = 5; + MobilityFeatures().stopDuration = Duration(seconds: 20); + MobilityFeatures().placeRadius = 50; + MobilityFeatures().stopRadius = 5.0; } ``` -### Step 2: Set up streaming -Location data collection is not directly supported by this package, for this you have to use a location plugin such as [`carp_background_location`](https://pub.dev/packages/carp_background_location). +Features computation is triggered when the user moves around and change their geo-position by a certain distance (stop distance). +If the stop was long enough (stop duration) the stop will be saved. Places are computed by grouping stops based on distance between them (place radius) + +Common for these parameters is that their value depend on what you are trying to capture. +Low parameter values will make the features more fine-grained but will trigger computation more often and will likely also lead to noisy features. +For example, given a low stop duration, stopping for a red light in traffic will count as a stop. Such granularity will be irrelevant for many use cases, but may be useful if questions such as "Do a user take the same route to work every day?" + -From here, you can to convert from whichever location object is used by the location plugin to a `LocationSample`. +### Step 2 - Set up location streaming -Next, you need to subscribe to the `MobilityFeatures()`'s `contextStream` to be be notified each time a new set of features has been computed. +Collection of location data is not directly supported by this package, for this you have to use a location plugin such as [`carp_background_location`](https://pub.dev/packages/carp_background_location). You can to convert from whichever location object is used by the location plugin to a `LocationSample` object. +Next, you can start listening to location updates and subscribe to the `MobilityFeatures()`'s `contextStream` to be be notified each time a new set of features has been computed. Below is shown an example using the [`carp_background_location`](https://pub.dev/packages/carp_background_location) plugin, where a `LocationDto` stream is converted into a `LocationSample` stream by using a map-function. ```dart -/// Start the streaming of location data and mobility features -void streamInit() async { - /// Get the location data stream (specific to mubs_background_location) + /// Set up streams: + /// * Location streaming to MobilityContext + /// * Subscribe to MobilityContext updates + void streamInit() async { locationStream = LocationManager().locationStream; - locationSubscription = locationStream.listen(onData); - - /// Start the location service (specific to mubs_background_location) + + // subscribe to location stream - in case this is needed in the app + if (locationSubscription != null) locationSubscription.cancel(); + locationSubscription = locationStream.listen(onLocationUpdate); + + // start the location service (specific to carp_background_location) await LocationManager().start(); - /// Convert from [LocationDto] to [LocationSample] - Stream locationSampleStream = locationStream.map((e) => - LocationSample(GeoLocation(e.latitude, e.longitude), DateTime.now())); + // map from [LocationDto] to [LocationSample] + Stream locationSampleStream = locationStream.map( + (location) => LocationSample( + GeoLocation(location.latitude, location.longitude), + DateTime.now())); - /// Provide the MobilityFactory instance with the LocationSample stream + // provide the [MobilityFeatures] instance with the LocationSample stream MobilityFeatures().startListening(locationSampleStream); - - /// Start listening to incoming MobilityContext objects - MobilityFeatures().contextStream.listen(onMobilityContext); -} + + // start listening to incoming MobilityContext objects + mobilitySubscription = + MobilityFeatures().contextStream.listen(onMobilityContext); + } + + /// Called whenever location changes. + void onLocationUpdate(LocationDto dto) { + print(dtoToString(dto)); + } ``` -### Step 3: Handle features +### Step 3 - Handle mobility + A call-back method is used to handle incoming MobilityContext objects: ```dart diff --git a/packages/mobility_features/example/.flutter-plugins-dependencies b/packages/mobility_features/example/.flutter-plugins-dependencies index f0dcf30b4..4c3dcc7f2 100644 --- a/packages/mobility_features/example/.flutter-plugins-dependencies +++ b/packages/mobility_features/example/.flutter-plugins-dependencies @@ -1 +1 @@ -{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"background_locator","path":"/Users/bardram/.pub-cache/hosted/pub.dartlang.org/background_locator-1.6.2+1-beta/","dependencies":[]},{"name":"geolocator","path":"/Users/bardram/.pub-cache/hosted/pub.dartlang.org/geolocator-6.2.1/","dependencies":[]},{"name":"location_permissions","path":"/Users/bardram/.pub-cache/hosted/pub.dartlang.org/location_permissions-4.0.0/","dependencies":[]},{"name":"path_provider","path":"/Users/bardram/.pub-cache/hosted/pub.dartlang.org/path_provider-2.0.2/","dependencies":[]}],"android":[{"name":"background_locator","path":"/Users/bardram/.pub-cache/hosted/pub.dartlang.org/background_locator-1.6.2+1-beta/","dependencies":[]},{"name":"geolocator","path":"/Users/bardram/.pub-cache/hosted/pub.dartlang.org/geolocator-6.2.1/","dependencies":[]},{"name":"location_permissions","path":"/Users/bardram/.pub-cache/hosted/pub.dartlang.org/location_permissions-4.0.0/","dependencies":[]},{"name":"path_provider","path":"/Users/bardram/.pub-cache/hosted/pub.dartlang.org/path_provider-2.0.2/","dependencies":[]}],"macos":[{"name":"path_provider_macos","path":"/Users/bardram/.pub-cache/hosted/pub.dartlang.org/path_provider_macos-2.0.0/","dependencies":[]}],"linux":[{"name":"path_provider_linux","path":"/Users/bardram/.pub-cache/hosted/pub.dartlang.org/path_provider_linux-2.0.0/","dependencies":[]}],"windows":[{"name":"path_provider_windows","path":"/Users/bardram/.pub-cache/hosted/pub.dartlang.org/path_provider_windows-2.0.1/","dependencies":[]}],"web":[{"name":"geolocator_web","path":"/Users/bardram/.pub-cache/hosted/pub.dartlang.org/geolocator_web-1.0.1/","dependencies":[]}]},"dependencyGraph":[{"name":"background_locator","dependencies":[]},{"name":"geolocator","dependencies":["geolocator_web"]},{"name":"geolocator_web","dependencies":[]},{"name":"location_permissions","dependencies":[]},{"name":"path_provider","dependencies":["path_provider_macos","path_provider_linux","path_provider_windows"]},{"name":"path_provider_linux","dependencies":[]},{"name":"path_provider_macos","dependencies":[]},{"name":"path_provider_windows","dependencies":[]}],"date_created":"2021-06-22 17:08:39.248479","version":"2.2.2"} \ No newline at end of file +{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"background_locator","path":"/Users/bardram/.pub-cache/hosted/pub.dartlang.org/background_locator-1.6.12/","dependencies":[]},{"name":"path_provider_ios","path":"/Users/bardram/.pub-cache/hosted/pub.dartlang.org/path_provider_ios-2.0.7/","dependencies":[]}],"android":[{"name":"background_locator","path":"/Users/bardram/.pub-cache/hosted/pub.dartlang.org/background_locator-1.6.12/","dependencies":[]},{"name":"path_provider_android","path":"/Users/bardram/.pub-cache/hosted/pub.dartlang.org/path_provider_android-2.0.11/","dependencies":[]}],"macos":[{"name":"path_provider_macos","path":"/Users/bardram/.pub-cache/hosted/pub.dartlang.org/path_provider_macos-2.0.4/","dependencies":[]}],"linux":[{"name":"path_provider_linux","path":"/Users/bardram/.pub-cache/hosted/pub.dartlang.org/path_provider_linux-2.1.4/","dependencies":[]}],"windows":[{"name":"path_provider_windows","path":"/Users/bardram/.pub-cache/hosted/pub.dartlang.org/path_provider_windows-2.0.4/","dependencies":[]}],"web":[]},"dependencyGraph":[{"name":"background_locator","dependencies":[]},{"name":"path_provider","dependencies":["path_provider_android","path_provider_ios","path_provider_linux","path_provider_macos","path_provider_windows"]},{"name":"path_provider_android","dependencies":[]},{"name":"path_provider_ios","dependencies":[]},{"name":"path_provider_linux","dependencies":[]},{"name":"path_provider_macos","dependencies":[]},{"name":"path_provider_windows","dependencies":[]}],"date_created":"2022-01-09 19:33:13.611264","version":"2.5.3"} \ No newline at end of file diff --git a/packages/mobility_features/example/README.md b/packages/mobility_features/example/README.md index a13562602..040129780 100644 --- a/packages/mobility_features/example/README.md +++ b/packages/mobility_features/example/README.md @@ -1,16 +1,3 @@ -# example +# Mobility Features Example App -A new Flutter project. - -## Getting Started - -This project is a starting point for a Flutter application. - -A few resources to get you started if this is your first Flutter project: - -- [Lab: Write your first Flutter app](https://flutter.dev/docs/get-started/codelab) -- [Cookbook: Useful Flutter samples](https://flutter.dev/docs/cookbook) - -For help getting started with Flutter, view our -[online documentation](https://flutter.dev/docs), which offers tutorials, -samples, guidance on mobile development, and a full API reference. +This is an example app for the Mobility Features package. diff --git a/packages/mobility_features/example/android/app/build.gradle b/packages/mobility_features/example/android/app/build.gradle index 320be2219..dcb69350b 100644 --- a/packages/mobility_features/example/android/app/build.gradle +++ b/packages/mobility_features/example/android/app/build.gradle @@ -26,7 +26,7 @@ apply plugin: 'kotlin-android' apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" android { - compileSdkVersion 28 + compileSdkVersion 30 sourceSets { main.java.srcDirs += 'src/main/kotlin' @@ -40,7 +40,7 @@ android { // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). applicationId "com.example.example" minSdkVersion 16 - targetSdkVersion 28 + targetSdkVersion 30 versionCode flutterVersionCode.toInteger() versionName flutterVersionName } diff --git a/packages/mobility_features/example/android/app/src/main/AndroidManifest.xml b/packages/mobility_features/example/android/app/src/main/AndroidManifest.xml index aa37b748b..bd1b230f6 100644 --- a/packages/mobility_features/example/android/app/src/main/AndroidManifest.xml +++ b/packages/mobility_features/example/android/app/src/main/AndroidManifest.xml @@ -16,7 +16,7 @@ + - --> + + + \ No newline at end of file diff --git a/packages/mobility_features/example/lib/main.dart b/packages/mobility_features/example/lib/main.dart index 3e1eddac4..d63cd2f87 100644 --- a/packages/mobility_features/example/lib/main.dart +++ b/packages/mobility_features/example/lib/main.dart @@ -22,9 +22,7 @@ Widget entry(String key, String value, Icon icon) { )); } -String formatDate(DateTime date) { - return '${date.year}/${date.month}/${date.day}'; -} +String formatDate(DateTime date) => '${date.year}/${date.month}/${date.day}'; String interval(DateTime a, DateTime b) { String pad(int x) => '${x.toString().padLeft(2, '0')}'; @@ -53,11 +51,11 @@ class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( - title: 'Mobility Features Demo', + title: 'Mobility Features Example', theme: ThemeData( primarySwatch: Colors.blue, ), - home: MyHomePage(title: 'Mobility Features Demo'), + home: MyHomePage(title: 'Mobility Features Example'), ); } } @@ -103,46 +101,53 @@ class _MyHomePageState extends State { streamInit(); } - @override - void dispose() { - mobilitySubscription?.cancel(); - super.dispose(); - } - - void onMobilityContext(MobilityContext context) { - print('Context received: ${context.toJson()}'); - setState(() { - _state = AppState.FEATURES_READY; - _mobilityContext = context; - }); - } - /// Set up streams: - /// * Subscribe to stream in case it is already running (Android only) + /// * Location streaming to MobilityContext /// * Subscribe to MobilityContext updates void streamInit() async { locationStream = LocationManager().locationStream; - locationSubscription = locationStream.listen(onData); - // Subscribe if it hasn't been done already - if (locationSubscription != null) { - locationSubscription.cancel(); - } - locationSubscription = locationStream.listen(onData); + // subscribe to location stream - in case this is needed in the app + if (locationSubscription != null) locationSubscription.cancel(); + locationSubscription = locationStream.listen(onLocationUpdate); + + // start the location service (specific to carp_background_location) await LocationManager().start(); - Stream locationSampleStream = locationStream.map((e) => - LocationSample(GeoLocation(e.latitude, e.longitude), DateTime.now())); + // map from [LocationDto] to [LocationSample] + Stream locationSampleStream = locationStream.map( + (location) => LocationSample( + GeoLocation(location.latitude, location.longitude), + DateTime.now())); + // provide the [MobilityFeatures] instance with the LocationSample stream MobilityFeatures().startListening(locationSampleStream); + + // start listening to incoming MobilityContext objects mobilitySubscription = MobilityFeatures().contextStream.listen(onMobilityContext); } - void onData(LocationDto dto) { + /// Called whenever location changes. + void onLocationUpdate(LocationDto dto) { print(dtoToString(dto)); } + /// Called whenever mobility context changes. + void onMobilityContext(MobilityContext context) { + print('Context received: ${context.toJson()}'); + setState(() { + _state = AppState.FEATURES_READY; + _mobilityContext = context; + }); + } + + @override + void dispose() { + mobilitySubscription?.cancel(); + super.dispose(); + } + Widget get featuresOverview { return ListView( children: [ diff --git a/packages/mobility_features/example/pubspec.yaml b/packages/mobility_features/example/pubspec.yaml index 863e892cd..9c766ee87 100644 --- a/packages/mobility_features/example/pubspec.yaml +++ b/packages/mobility_features/example/pubspec.yaml @@ -1,21 +1,7 @@ -name: example -description: A new Flutter project. - -# The following line prevents the package from being accidentally published to -# pub.dev using `pub publish`. This is preferred for private packages. +name: mobility_features_example +description: An example app for the Mobility Features package. publish_to: 'none' # Remove this line if you wish to publish to pub.dev - -# The following defines the version and build number for your application. -# A version number is three numbers separated by dots, like 1.2.43 -# followed by an optional build number separated by a +. -# Both the version and the builder number may be overridden in flutter -# build by specifying --build-name and --build-number, respectively. -# In Android, build-name is used as versionName while build-number used as versionCode. -# Read more about Android versioning at https://developer.android.com/studio/publish/versioning -# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. -# Read more about iOS versioning at -# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html -version: 1.0.0+1 +version: 3.1.0 environment: sdk: ">=2.7.0 <3.0.0" @@ -24,59 +10,16 @@ dependencies: flutter: sdk: flutter - geolocator: mobility_features: path: ../ - carp_background_location: - path: ../../carp_background_location - + carp_background_location: ^3.0.1 + # path: ../../carp_background_location - # The following adds the Cupertino Icons font to your application. - # Use with the CupertinoIcons class for iOS style icons. cupertino_icons: ^0.1.3 dev_dependencies: flutter_test: sdk: flutter -# For information on the generic Dart part of this file, see the -# following page: https://dart.dev/tools/pub/pubspec - -# The following section is specific to Flutter. flutter: - - # The following line ensures that the Material Icons font is - # included with your application, so that you can use the icons in - # the material Icons class. uses-material-design: true - - # To add assets to your application, add an assets section, like this: - # assets: - # - images/a_dot_burr.jpeg - # - images/a_dot_ham.jpeg - - # An image asset can refer to one or more resolution-specific "variants", see - # https://flutter.dev/assets-and-images/#resolution-aware. - - # For details regarding adding assets from package dependencies, see - # https://flutter.dev/assets-and-images/#from-packages - - # To add custom fonts to your application, add a fonts section here, - # in this "flutter" section. Each entry in this list should have a - # "family" key with the font family name, and a "fonts" key with a - # list giving the asset and other descriptors for the font. For - # example: - # fonts: - # - family: Schyler - # fonts: - # - asset: fonts/Schyler-Regular.ttf - # - asset: fonts/Schyler-Italic.ttf - # style: italic - # - family: Trajan Pro - # fonts: - # - asset: fonts/TrajanPro.ttf - # - asset: fonts/TrajanPro_Bold.ttf - # weight: 700 - # - # For details regarding fonts from package dependencies, - # see https://flutter.dev/custom-fonts/#from-packages diff --git a/packages/mobility_features/lib/mobility_features.dart b/packages/mobility_features/lib/mobility_features.dart index 0e6d4e867..7dc0eea0c 100644 --- a/packages/mobility_features/lib/mobility_features.dart +++ b/packages/mobility_features/lib/mobility_features.dart @@ -14,5 +14,5 @@ part 'package:mobility_features/src/mobility_domain.dart'; part 'package:mobility_features/src/mobility_intermediate.dart'; part 'package:mobility_features/src/mobility_context.dart'; part 'package:mobility_features/src/mobility_serializer.dart'; -part 'package:mobility_features/src/mobility_factory.dart'; +part 'package:mobility_features/src/mobility_features.dart'; part 'package:mobility_features/src/mobility_file_util.dart'; diff --git a/packages/mobility_features/lib/src/mobility_context.dart b/packages/mobility_features/lib/src/mobility_context.dart index 245c0ea67..f8ed408c3 100644 --- a/packages/mobility_features/lib/src/mobility_context.dart +++ b/packages/mobility_features/lib/src/mobility_context.dart @@ -1,20 +1,19 @@ part of mobility_features; /// Daily mobility context. -/// All Stops and Moves should be on the same date. -/// Places are all places for which the duration -/// on the given data is greater than 0 +/// +/// All Stops and Moves are on the same [date]. +/// [places] are all places for which the duration on the given date is greater than 0. class MobilityContext { + late DateTime _timestamp, _date; List _stops; - List? _places, _significantPlaces; + List _places; + List _moves; + late List _significantPlaces; Place? _homePlace; - List _moves; - DateTime? _timestamp, _date; late _HourMatrix _hourMatrix; - /// Features - int? _numberOfPlaces; double? _locationVariance, _entropy, _normalizedEntropy, @@ -23,83 +22,77 @@ class MobilityContext { List? contexts; /// Private constructor, cannot be instantiated from outside - MobilityContext._(this._stops, this._places, this._moves, this._date, - {this.contexts}) { + MobilityContext._( + this._stops, + this._places, + this._moves, + this._date, { + this.contexts, + }) { _timestamp = DateTime.now(); - // If contexts array is null, init to empty array + // if contexts array is null, init to empty array contexts = contexts ?? []; + // compute all the features _significantPlaces = - _places!.where((p) => p.duration > Duration(minutes: 3)).toList(); - - // Compute all the features - _numberOfPlaces = _calculateNumberOfSignificantPlaces(); - - _hourMatrix = _HourMatrix.fromStops(_stops, _places!.length); - + _places.where((p) => p.duration > Duration(minutes: 3)).toList(); + _hourMatrix = _HourMatrix.fromStops(_stops, _places.length); _homePlace = _findHomePlaceToday(); - _homeStay = _calculateHomeStay(); - _locationVariance = _calculateLocationVariance(); - _entropy = _calculateEntropy(); - _normalizedEntropy = _calculateNormalizedEntropy(); - _distanceTravelled = _calculateDistanceTravelled(); } - // Get the date of the context - DateTime? get date => _date; + // The date of this context. + DateTime get date => _date; - /// Get stops today + /// Timestamp at which the features were computed + DateTime get timestamp => _timestamp; + + /// Stops today. List get stops => _stops; - /// Get moves today + /// Moves today. List get moves => _moves; - /// Get places today - List? get places => _places; + /// Places today. + List get places => _places; - /// Get all the significant places (places with a minimum duration) - List? get significantPlaces => _significantPlaces; + /// All significant places, i.e. places with a minimum stay duration. + List get significantPlaces => _significantPlaces; - /// Get the timestamp at which the features were computed - DateTime? get timestamp => _timestamp; + /// Number of significant places visited today. + int get numberOfSignificantPlaces => _significantPlaces.length; - /// Get the home place cluster + /// Home place. + /// Returns null if home cannot be found from the available data. Place? get homePlace => _homePlace; - /// Number of Places today - int? get numberOfSignificantPlaces => _numberOfPlaces; - - /// Home Stay Percentage today - /// A scalar between 0 and 1, i.e. from 0% to 100% + /// Home Stay Percentage today. A scalar between 0 and 1. + /// Returns null if cannot be calculated based on the available data. double? get homeStay => _homeStay; - /// Location Variance today + /// Location variance today. double? get locationVariance => _locationVariance; - /// Entropy - /// High entropy: Time is spent evenly among all places - /// Low entropy: Time is mainly spent at a few of the places + /// Location entropy. + /// + /// * High entropy: Time is spent evenly among all places + /// * Low entropy: Time is mainly spent at a few of the places double? get entropy => _entropy; - /// Normalized entropy, - /// a scalar between 0 and 1 + /// Normalized location entropy. A scalar between 0 and 1. double? get normalizedEntropy => _normalizedEntropy; - /// Distance travelled today, in meters + /// Distance travelled today in meters. double? get distanceTravelled => _distanceTravelled; - /// Private number of places calculation - int _calculateNumberOfSignificantPlaces() => significantPlaces!.length; - /// Private home stay calculation - double _calculateHomeStay() { - if (stops.isEmpty) return -1.0; + double? _calculateHomeStay() { + if (stops.isEmpty) return null; // Latest known sample time DateTime latestTime = _stops.last.departure; @@ -108,10 +101,8 @@ class MobilityContext { int totalTime = latestTime.millisecondsSinceEpoch - latestTime.midnight.millisecondsSinceEpoch; - // Find todays home id, if no home exists today return -1.0 - if (_hourMatrix.homePlaceId == -1) { - return -1.0; - } + // Find todays home id, if no home exists today return null + if (_hourMatrix.homePlaceId == -1) return null; int homeTime = stops .where((s) => s.placeId == _hourMatrix.homePlaceId) @@ -121,20 +112,15 @@ class MobilityContext { return homeTime.toDouble() / totalTime.toDouble(); } - Place? _findHomePlaceToday() { - int home = _hourMatrix.homePlaceId; - if (home == -1) { - return null; - } - return _places!.where((p) => p.id == _hourMatrix.homePlaceId).first; - } + Place? _findHomePlaceToday() => (_hourMatrix.homePlaceId == -1) + ? null + : _places.where((p) => p.id == _hourMatrix.homePlaceId).first; /// Location variance calculation - double _calculateLocationVariance() { + double? _calculateLocationVariance() { // Require at least 2 observations - if (_stops.length < 2) { - return 0.0; - } + if (_stops.length < 2) return 0.0; + double latStd = Stats.fromData(_stops.map((s) => (s.geoLocation.latitude))) .standardDeviation as double; @@ -143,18 +129,16 @@ class MobilityContext { return log(latStd * latStd + lonStd * lonStd + 1); } - double _calculateEntropy() { - // If no places were visited return -1.0 - if (places!.isEmpty) { - return -1.0; - } - // The Entropy is zero when one outcome is certain to occur. - else if (places!.length == 1) { - return 0.0; - } - // Calculate time spent at different places + double? _calculateEntropy() { + // if no places were visited return null + if (places.isEmpty) + return null; + // the Entropy is zero when one outcome is certain to occur + else if (places.length == 1) return 0.0; + + // calculate time spent at different places List durations = - places!.map((p) => p.durationForDate(date)).toList(); + places.map((p) => p.durationForDate(date)).toList(); Duration totalTimeSpent = durations.fold(Duration(), (a, b) => a + b); @@ -167,24 +151,19 @@ class MobilityContext { } /// Private normalized entropy calculation - double _calculateNormalizedEntropy() { - if (places!.length == 1) { - return 0.0; - } - return entropy! / log(places!.length); - } + double _calculateNormalizedEntropy() => + (places.length == 1) ? 0.0 : entropy! / log(places.length); /// Private distance travelled calculation - double _calculateDistanceTravelled() { - return _moves.map((m) => (m.distance)).fold(0.0, (a, b) => a + b!); - } + double _calculateDistanceTravelled() => + _moves.map((m) => (m.distance)).fold(0.0, (a, b) => a + b!); Map toJson() => { - "date": date!.toIso8601String(), - "computed_at": timestamp!.toIso8601String(), + "date": date.toIso8601String(), + "computed_at": timestamp.toIso8601String(), "num_of_stops": stops.length, "num_of_moves": moves.length, - "num_of_significant_places": significantPlaces!.length, + "num_of_significant_places": significantPlaces.length, "normalized_entropy": normalizedEntropy, "home_stay": homeStay, "distance_travelled": distanceTravelled, diff --git a/packages/mobility_features/lib/src/mobility_domain.dart b/packages/mobility_features/lib/src/mobility_domain.dart index 67aff2b99..b48fa1b5d 100644 --- a/packages/mobility_features/lib/src/mobility_domain.dart +++ b/packages/mobility_features/lib/src/mobility_domain.dart @@ -16,7 +16,6 @@ const String _LATITUDE = 'latitude', /// to serialize and deserialize an object abstract class _Serializable { Map toJson(); - _Serializable._fromJson(Map json); } diff --git a/packages/mobility_features/lib/src/mobility_factory.dart b/packages/mobility_features/lib/src/mobility_features.dart similarity index 100% rename from packages/mobility_features/lib/src/mobility_factory.dart rename to packages/mobility_features/lib/src/mobility_features.dart diff --git a/packages/mobility_features/mobility_features_example/.flutter-plugins-dependencies b/packages/mobility_features/mobility_features_example/.flutter-plugins-dependencies deleted file mode 100644 index d2dc30c27..000000000 --- a/packages/mobility_features/mobility_features_example/.flutter-plugins-dependencies +++ /dev/null @@ -1 +0,0 @@ -{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"background_locator","path":"/Users/bardram/.pub-cache/hosted/pub.dartlang.org/background_locator-1.6.0+1-beta/","dependencies":[]},{"name":"geolocator","path":"/Users/bardram/.pub-cache/hosted/pub.dartlang.org/geolocator-6.2.1/","dependencies":[]},{"name":"location_permissions","path":"/Users/bardram/.pub-cache/hosted/pub.dartlang.org/location_permissions-3.0.0+1/","dependencies":[]},{"name":"path_provider","path":"/Users/bardram/.pub-cache/hosted/pub.dartlang.org/path_provider-1.6.28/","dependencies":[]}],"android":[{"name":"background_locator","path":"/Users/bardram/.pub-cache/hosted/pub.dartlang.org/background_locator-1.6.0+1-beta/","dependencies":[]},{"name":"geolocator","path":"/Users/bardram/.pub-cache/hosted/pub.dartlang.org/geolocator-6.2.1/","dependencies":[]},{"name":"location_permissions","path":"/Users/bardram/.pub-cache/hosted/pub.dartlang.org/location_permissions-3.0.0+1/","dependencies":[]},{"name":"path_provider","path":"/Users/bardram/.pub-cache/hosted/pub.dartlang.org/path_provider-1.6.28/","dependencies":[]}],"macos":[{"name":"path_provider_macos","path":"/Users/bardram/.pub-cache/hosted/pub.dartlang.org/path_provider_macos-0.0.4+8/","dependencies":[]}],"linux":[{"name":"path_provider_linux","path":"/Users/bardram/.pub-cache/hosted/pub.dartlang.org/path_provider_linux-0.0.1+2/","dependencies":[]}],"windows":[{"name":"path_provider_windows","path":"/Users/bardram/.pub-cache/hosted/pub.dartlang.org/path_provider_windows-0.0.5/","dependencies":[]}],"web":[{"name":"geolocator_web","path":"/Users/bardram/.pub-cache/hosted/pub.dartlang.org/geolocator_web-1.0.1/","dependencies":[]}]},"dependencyGraph":[{"name":"background_locator","dependencies":[]},{"name":"geolocator","dependencies":["geolocator_web"]},{"name":"geolocator_web","dependencies":[]},{"name":"location_permissions","dependencies":[]},{"name":"path_provider","dependencies":["path_provider_macos","path_provider_linux","path_provider_windows"]},{"name":"path_provider_linux","dependencies":[]},{"name":"path_provider_macos","dependencies":[]},{"name":"path_provider_windows","dependencies":[]}],"date_created":"2021-06-22 10:23:03.000927","version":"2.2.2"} \ No newline at end of file diff --git a/packages/mobility_features/mobility_features_example/.metadata b/packages/mobility_features/mobility_features_example/.metadata deleted file mode 100644 index ade6bc987..000000000 --- a/packages/mobility_features/mobility_features_example/.metadata +++ /dev/null @@ -1,10 +0,0 @@ -# This file tracks properties of this Flutter project. -# Used by Flutter tool to assess capabilities and perform upgrades etc. -# -# This file should be version controlled and should not be manually edited. - -version: - revision: 8af6b2f038c1172e61d418869363a28dffec3cb4 - channel: stable - -project_type: app diff --git a/packages/mobility_features/mobility_features_example/README.md b/packages/mobility_features/mobility_features_example/README.md deleted file mode 100644 index 5334c365a..000000000 --- a/packages/mobility_features/mobility_features_example/README.md +++ /dev/null @@ -1,16 +0,0 @@ -# mobility_features_example - -A new Flutter project. - -## Getting Started - -This project is a starting point for a Flutter application. - -A few resources to get you started if this is your first Flutter project: - -- [Lab: Write your first Flutter app](https://flutter.dev/docs/get-started/codelab) -- [Cookbook: Useful Flutter samples](https://flutter.dev/docs/cookbook) - -For help getting started with Flutter, view our -[online documentation](https://flutter.dev/docs), which offers tutorials, -samples, guidance on mobile development, and a full API reference. diff --git a/packages/mobility_features/mobility_features_example/android/app/build.gradle b/packages/mobility_features/mobility_features_example/android/app/build.gradle deleted file mode 100644 index 3b4f63431..000000000 --- a/packages/mobility_features/mobility_features_example/android/app/build.gradle +++ /dev/null @@ -1,63 +0,0 @@ -def localProperties = new Properties() -def localPropertiesFile = rootProject.file('local.properties') -if (localPropertiesFile.exists()) { - localPropertiesFile.withReader('UTF-8') { reader -> - localProperties.load(reader) - } -} - -def flutterRoot = localProperties.getProperty('flutter.sdk') -if (flutterRoot == null) { - throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") -} - -def flutterVersionCode = localProperties.getProperty('flutter.versionCode') -if (flutterVersionCode == null) { - flutterVersionCode = '1' -} - -def flutterVersionName = localProperties.getProperty('flutter.versionName') -if (flutterVersionName == null) { - flutterVersionName = '1.0' -} - -apply plugin: 'com.android.application' -apply plugin: 'kotlin-android' -apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" - -android { - compileSdkVersion 28 - - sourceSets { - main.java.srcDirs += 'src/main/kotlin' - } - - lintOptions { - disable 'InvalidPackage' - } - - defaultConfig { - // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). - applicationId "com.example.mobility_features_example" - minSdkVersion 16 - targetSdkVersion 28 - versionCode flutterVersionCode.toInteger() - versionName flutterVersionName - } - - buildTypes { - release { - // TODO: Add your own signing config for the release build. - // Signing with the debug keys for now, so `flutter run --release` works. - signingConfig signingConfigs.debug - } - } -} - -flutter { - source '../..' -} - -dependencies { - implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" -} diff --git a/packages/mobility_features/mobility_features_example/android/app/src/debug/AndroidManifest.xml b/packages/mobility_features/mobility_features_example/android/app/src/debug/AndroidManifest.xml deleted file mode 100644 index 2204da101..000000000 --- a/packages/mobility_features/mobility_features_example/android/app/src/debug/AndroidManifest.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - diff --git a/packages/mobility_features/mobility_features_example/android/app/src/main/AndroidManifest.xml b/packages/mobility_features/mobility_features_example/android/app/src/main/AndroidManifest.xml deleted file mode 100644 index 95b2aa9f3..000000000 --- a/packages/mobility_features/mobility_features_example/android/app/src/main/AndroidManifest.xml +++ /dev/null @@ -1,76 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/packages/mobility_features/mobility_features_example/android/app/src/main/kotlin/com/example/mobility_features_example/MainActivity.kt b/packages/mobility_features/mobility_features_example/android/app/src/main/kotlin/com/example/mobility_features_example/MainActivity.kt deleted file mode 100644 index 2fec82071..000000000 --- a/packages/mobility_features/mobility_features_example/android/app/src/main/kotlin/com/example/mobility_features_example/MainActivity.kt +++ /dev/null @@ -1,6 +0,0 @@ -package com.example.mobility_features_example - -import io.flutter.embedding.android.FlutterActivity - -class MainActivity: FlutterActivity() { -} diff --git a/packages/mobility_features/mobility_features_example/android/app/src/main/res/drawable/launch_background.xml b/packages/mobility_features/mobility_features_example/android/app/src/main/res/drawable/launch_background.xml deleted file mode 100644 index 304732f88..000000000 --- a/packages/mobility_features/mobility_features_example/android/app/src/main/res/drawable/launch_background.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - diff --git a/packages/mobility_features/mobility_features_example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/packages/mobility_features/mobility_features_example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png deleted file mode 100644 index db77bb4b7b0906d62b1847e87f15cdcacf6a4f29..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 544 zcmeAS@N?(olHy`uVBq!ia0vp^9w5xY3?!3`olAj~WQl7;NpOBzNqJ&XDuZK6ep0G} zXKrG8YEWuoN@d~6R2!h8bpbvhu0Wd6uZuB!w&u2PAxD2eNXD>P5D~Wn-+_Wa#27Xc zC?Zj|6r#X(-D3u$NCt}(Ms06KgJ4FxJVv{GM)!I~&n8Bnc94O7-Hd)cjDZswgC;Qs zO=b+9!WcT8F?0rF7!Uys2bs@gozCP?z~o%U|N3vA*22NaGQG zlg@K`O_XuxvZ&Ks^m&R!`&1=spLvfx7oGDKDwpwW`#iqdw@AL`7MR}m`rwr|mZgU`8P7SBkL78fFf!WnuYWm$5Z0 zNXhDbCv&49sM544K|?c)WrFfiZvCi9h0O)B3Pgg&ebxsLQ05GG~ AQ2+n{ diff --git a/packages/mobility_features/mobility_features_example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/packages/mobility_features/mobility_features_example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png deleted file mode 100644 index 17987b79bb8a35cc66c3c1fd44f5a5526c1b78be..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 442 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA3?vioaBc-sk|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*D5Xx&nMcT!A!W`0S9QKQy;}1Cl^CgaH=;G9cpY;r$Q>i*pfB zP2drbID<_#qf;rPZx^FqH)F_D#*k@@q03KywUtLX8Ua?`H+NMzkczFPK3lFz@i_kW%1NOn0|D2I9n9wzH8m|-tHjsw|9>@K=iMBhxvkv6m8Y-l zytQ?X=U+MF$@3 zt`~i=@j|6y)RWMK--}M|=T`o&^Ni>IoWKHEbBXz7?A@mgWoL>!*SXo`SZH-*HSdS+ yn*9;$7;m`l>wYBC5bq;=U}IMqLzqbYCidGC!)_gkIk_C@Uy!y&wkt5C($~2D>~)O*cj@FGjOCM)M>_ixfudOh)?xMu#Fs z#}Y=@YDTwOM)x{K_j*Q;dPdJ?Mz0n|pLRx{4n|)f>SXlmV)XB04CrSJn#dS5nK2lM zrZ9#~WelCp7&e13Y$jvaEXHskn$2V!!DN-nWS__6T*l;H&Fopn?A6HZ-6WRLFP=R` zqG+CE#d4|IbyAI+rJJ`&x9*T`+a=p|0O(+s{UBcyZdkhj=yS1>AirP+0R;mf2uMgM zC}@~JfByORAh4SyRgi&!(cja>F(l*O+nd+@4m$|6K6KDn_&uvCpV23&>G9HJp{xgg zoq1^2_p9@|WEo z*X_Uko@K)qYYv~>43eQGMdbiGbo>E~Q& zrYBH{QP^@Sti!`2)uG{irBBq@y*$B zi#&(U-*=fp74j)RyIw49+0MRPMRU)+a2r*PJ$L5roHt2$UjExCTZSbq%V!HeS7J$N zdG@vOZB4v_lF7Plrx+hxo7(fCV&}fHq)$ diff --git a/packages/mobility_features/mobility_features_example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/packages/mobility_features/mobility_features_example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png deleted file mode 100644 index d5f1c8d34e7a88e3f88bea192c3a370d44689c3c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1031 zcmeAS@N?(olHy`uVBq!ia0vp^6F``Q8Ax83A=Cw=BuiW)N`mv#O3D+9QW+dm@{>{( zJaZG%Q-e|yQz{EjrrIztFa`(sgt!6~Yi|1%a`XoT0ojZ}lNrNjb9xjc(B0U1_% zz5^97Xt*%oq$rQy4?0GKNfJ44uvxI)gC`h-NZ|&0-7(qS@?b!5r36oQ}zyZrNO3 zMO=Or+<~>+A&uN&E!^Sl+>xE!QC-|oJv`ApDhqC^EWD|@=#J`=d#Xzxs4ah}w&Jnc z$|q_opQ^2TrnVZ0o~wh<3t%W&flvYGe#$xqda2bR_R zvPYgMcHgjZ5nSA^lJr%;<&0do;O^tDDh~=pIxA#coaCY>&N%M2^tq^U%3DB@ynvKo}b?yu-bFc-u0JHzced$sg7S3zqI(2 z#Km{dPr7I=pQ5>FuK#)QwK?Y`E`B?nP+}U)I#c1+FM*1kNvWG|a(TpksZQ3B@sD~b zpQ2)*V*TdwjFOtHvV|;OsiDqHi=6%)o4b!)x$)%9pGTsE z-JL={-Ffv+T87W(Xpooq<`r*VzWQcgBN$$`u}f>-ZQI1BB8ykN*=e4rIsJx9>z}*o zo~|9I;xof diff --git a/packages/mobility_features/mobility_features_example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/packages/mobility_features/mobility_features_example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png deleted file mode 100644 index 4d6372eebdb28e45604e46eeda8dd24651419bc0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1443 zcmb`G{WsKk6vsdJTdFg%tJav9_E4vzrOaqkWF|A724Nly!y+?N9`YV6wZ}5(X(D_N(?!*n3`|_r0Hc?=PQw&*vnU?QTFY zB_MsH|!j$PP;I}?dppoE_gA(4uc!jV&0!l7_;&p2^pxNo>PEcNJv za5_RT$o2Mf!<+r?&EbHH6nMoTsDOa;mN(wv8RNsHpG)`^ymG-S5By8=l9iVXzN_eG%Xg2@Xeq76tTZ*dGh~Lo9vl;Zfs+W#BydUw zCkZ$o1LqWQO$FC9aKlLl*7x9^0q%0}$OMlp@Kk_jHXOjofdePND+j!A{q!8~Jn+s3 z?~~w@4?egS02}8NuulUA=L~QQfm;MzCGd)XhiftT;+zFO&JVyp2mBww?;QByS_1w! zrQlx%{^cMj0|Bo1FjwY@Q8?Hx0cIPF*@-ZRFpPc#bBw{5@tD(5%sClzIfl8WU~V#u zm5Q;_F!wa$BSpqhN>W@2De?TKWR*!ujY;Yylk_X5#~V!L*Gw~;$%4Q8~Mad z@`-kG?yb$a9cHIApZDVZ^U6Xkp<*4rU82O7%}0jjHlK{id@?-wpN*fCHXyXh(bLt* zPc}H-x0e4E&nQ>y%B-(EL=9}RyC%MyX=upHuFhAk&MLbsF0LP-q`XnH78@fT+pKPW zu72MW`|?8ht^tz$iC}ZwLp4tB;Q49K!QCF3@!iB1qOI=?w z7In!}F~ij(18UYUjnbmC!qKhPo%24?8U1x{7o(+?^Zu0Hx81|FuS?bJ0jgBhEMzf< zCgUq7r2OCB(`XkKcN-TL>u5y#dD6D!)5W?`O5)V^>jb)P)GBdy%t$uUMpf$SNV31$ zb||OojAbvMP?T@$h_ZiFLFVHDmbyMhJF|-_)HX3%m=CDI+ID$0^C>kzxprBW)hw(v zr!Gmda);ICoQyhV_oP5+C%?jcG8v+D@9f?Dk*!BxY}dazmrT@64UrP3hlslANK)bq z$67n83eh}OeW&SV@HG95P|bjfqJ7gw$e+`Hxo!4cx`jdK1bJ>YDSpGKLPZ^1cv$ek zIB?0S<#tX?SJCLWdMd{-ME?$hc7A$zBOdIJ)4!KcAwb=VMov)nK;9z>x~rfT1>dS+ zZ6#`2v@`jgbqq)P22H)Tx2CpmM^o1$B+xT6`(v%5xJ(?j#>Q$+rx_R|7TzDZe{J6q zG1*EcU%tE?!kO%^M;3aM6JN*LAKUVb^xz8-Pxo#jR5(-KBeLJvA@-gxNHx0M-ZJLl z;#JwQoh~9V?`UVo#}{6ka@II>++D@%KqGpMdlQ}?9E*wFcf5(#XQnP$Dk5~%iX^>f z%$y;?M0BLp{O3a(-4A?ewryHrrD%cx#Q^%KY1H zNre$ve+vceSLZcNY4U(RBX&)oZn*Py()h)XkE?PL$!bNb{N5FVI2Y%LKEm%yvpyTP z(1P?z~7YxD~Rf<(a@_y` diff --git a/packages/mobility_features/mobility_features_example/android/app/src/main/res/values/styles.xml b/packages/mobility_features/mobility_features_example/android/app/src/main/res/values/styles.xml deleted file mode 100644 index 1f83a33fd..000000000 --- a/packages/mobility_features/mobility_features_example/android/app/src/main/res/values/styles.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - diff --git a/packages/mobility_features/mobility_features_example/android/app/src/profile/AndroidManifest.xml b/packages/mobility_features/mobility_features_example/android/app/src/profile/AndroidManifest.xml deleted file mode 100644 index 2204da101..000000000 --- a/packages/mobility_features/mobility_features_example/android/app/src/profile/AndroidManifest.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - diff --git a/packages/mobility_features/mobility_features_example/android/build.gradle b/packages/mobility_features/mobility_features_example/android/build.gradle deleted file mode 100644 index 3100ad2d5..000000000 --- a/packages/mobility_features/mobility_features_example/android/build.gradle +++ /dev/null @@ -1,31 +0,0 @@ -buildscript { - ext.kotlin_version = '1.3.50' - repositories { - google() - jcenter() - } - - dependencies { - classpath 'com.android.tools.build:gradle:3.5.0' - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" - } -} - -allprojects { - repositories { - google() - jcenter() - } -} - -rootProject.buildDir = '../build' -subprojects { - project.buildDir = "${rootProject.buildDir}/${project.name}" -} -subprojects { - project.evaluationDependsOn(':app') -} - -task clean(type: Delete) { - delete rootProject.buildDir -} diff --git a/packages/mobility_features/mobility_features_example/android/gradle.properties b/packages/mobility_features/mobility_features_example/android/gradle.properties deleted file mode 100644 index 38c8d4544..000000000 --- a/packages/mobility_features/mobility_features_example/android/gradle.properties +++ /dev/null @@ -1,4 +0,0 @@ -org.gradle.jvmargs=-Xmx1536M -android.enableR8=true -android.useAndroidX=true -android.enableJetifier=true diff --git a/packages/mobility_features/mobility_features_example/android/gradle/wrapper/gradle-wrapper.properties b/packages/mobility_features/mobility_features_example/android/gradle/wrapper/gradle-wrapper.properties deleted file mode 100644 index 296b146b7..000000000 --- a/packages/mobility_features/mobility_features_example/android/gradle/wrapper/gradle-wrapper.properties +++ /dev/null @@ -1,6 +0,0 @@ -#Fri Jun 23 08:50:38 CEST 2017 -distributionBase=GRADLE_USER_HOME -distributionPath=wrapper/dists -zipStoreBase=GRADLE_USER_HOME -zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip diff --git a/packages/mobility_features/mobility_features_example/android/settings.gradle b/packages/mobility_features/mobility_features_example/android/settings.gradle deleted file mode 100644 index d3b6a4013..000000000 --- a/packages/mobility_features/mobility_features_example/android/settings.gradle +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright 2014 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -include ':app' - -def localPropertiesFile = new File(rootProject.projectDir, "local.properties") -def properties = new Properties() - -assert localPropertiesFile.exists() -localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) } - -def flutterSdkPath = properties.getProperty("flutter.sdk") -assert flutterSdkPath != null, "flutter.sdk not set in local.properties" -apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle" diff --git a/packages/mobility_features/mobility_features_example/ios/Flutter/AppFrameworkInfo.plist b/packages/mobility_features/mobility_features_example/ios/Flutter/AppFrameworkInfo.plist deleted file mode 100644 index 6b4c0f78a..000000000 --- a/packages/mobility_features/mobility_features_example/ios/Flutter/AppFrameworkInfo.plist +++ /dev/null @@ -1,26 +0,0 @@ - - - - - CFBundleDevelopmentRegion - $(DEVELOPMENT_LANGUAGE) - CFBundleExecutable - App - CFBundleIdentifier - io.flutter.flutter.app - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - App - CFBundlePackageType - FMWK - CFBundleShortVersionString - 1.0 - CFBundleSignature - ???? - CFBundleVersion - 1.0 - MinimumOSVersion - 8.0 - - diff --git a/packages/mobility_features/mobility_features_example/ios/Flutter/Debug.xcconfig b/packages/mobility_features/mobility_features_example/ios/Flutter/Debug.xcconfig deleted file mode 100644 index e8efba114..000000000 --- a/packages/mobility_features/mobility_features_example/ios/Flutter/Debug.xcconfig +++ /dev/null @@ -1,2 +0,0 @@ -#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" -#include "Generated.xcconfig" diff --git a/packages/mobility_features/mobility_features_example/ios/Flutter/Release.xcconfig b/packages/mobility_features/mobility_features_example/ios/Flutter/Release.xcconfig deleted file mode 100644 index 399e9340e..000000000 --- a/packages/mobility_features/mobility_features_example/ios/Flutter/Release.xcconfig +++ /dev/null @@ -1,2 +0,0 @@ -#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" -#include "Generated.xcconfig" diff --git a/packages/mobility_features/mobility_features_example/ios/Runner.xcodeproj/project.pbxproj b/packages/mobility_features/mobility_features_example/ios/Runner.xcodeproj/project.pbxproj deleted file mode 100644 index e87f87d3a..000000000 --- a/packages/mobility_features/mobility_features_example/ios/Runner.xcodeproj/project.pbxproj +++ /dev/null @@ -1,586 +0,0 @@ -// !$*UTF8*$! -{ - archiveVersion = 1; - classes = { - }; - objectVersion = 51; - objects = { - -/* Begin PBXBuildFile section */ - 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; - 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; - 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; - 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; - 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; - 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; - BDC7D7E50844DD8EC1242043 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0EF0191EA9C97779E7CB73A3 /* Pods_Runner.framework */; }; -/* End PBXBuildFile section */ - -/* Begin PBXCopyFilesBuildPhase section */ - 9705A1C41CF9048500538489 /* Embed Frameworks */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 2147483647; - dstPath = ""; - dstSubfolderSpec = 10; - files = ( - ); - name = "Embed Frameworks"; - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXCopyFilesBuildPhase section */ - -/* Begin PBXFileReference section */ - 0EF0191EA9C97779E7CB73A3 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; - 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; - 2F7C27A55D006E15F885EE70 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; - 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; - 61830B0A94CB8C7BCC2C8806 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; - 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; - 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; - 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; - 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; - 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; - 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; - 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; - 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; - 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; - 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - F01B23F0CD7B78D9F46B4E71 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; -/* End PBXFileReference section */ - -/* Begin PBXFrameworksBuildPhase section */ - 97C146EB1CF9000F007C117D /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - BDC7D7E50844DD8EC1242043 /* Pods_Runner.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXFrameworksBuildPhase section */ - -/* Begin PBXGroup section */ - 2E6C788C2D3A0AC7C13D602B /* Pods */ = { - isa = PBXGroup; - children = ( - 61830B0A94CB8C7BCC2C8806 /* Pods-Runner.debug.xcconfig */, - F01B23F0CD7B78D9F46B4E71 /* Pods-Runner.release.xcconfig */, - 2F7C27A55D006E15F885EE70 /* Pods-Runner.profile.xcconfig */, - ); - path = Pods; - sourceTree = ""; - }; - 9740EEB11CF90186004384FC /* Flutter */ = { - isa = PBXGroup; - children = ( - 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, - 9740EEB21CF90195004384FC /* Debug.xcconfig */, - 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, - 9740EEB31CF90195004384FC /* Generated.xcconfig */, - ); - name = Flutter; - sourceTree = ""; - }; - 97C146E51CF9000F007C117D = { - isa = PBXGroup; - children = ( - 9740EEB11CF90186004384FC /* Flutter */, - 97C146F01CF9000F007C117D /* Runner */, - 97C146EF1CF9000F007C117D /* Products */, - 2E6C788C2D3A0AC7C13D602B /* Pods */, - B70E9F531CE9FBE99B702E55 /* Frameworks */, - ); - sourceTree = ""; - }; - 97C146EF1CF9000F007C117D /* Products */ = { - isa = PBXGroup; - children = ( - 97C146EE1CF9000F007C117D /* Runner.app */, - ); - name = Products; - sourceTree = ""; - }; - 97C146F01CF9000F007C117D /* Runner */ = { - isa = PBXGroup; - children = ( - 97C146FA1CF9000F007C117D /* Main.storyboard */, - 97C146FD1CF9000F007C117D /* Assets.xcassets */, - 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, - 97C147021CF9000F007C117D /* Info.plist */, - 97C146F11CF9000F007C117D /* Supporting Files */, - 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, - 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, - 74858FAE1ED2DC5600515810 /* AppDelegate.swift */, - 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */, - ); - path = Runner; - sourceTree = ""; - }; - 97C146F11CF9000F007C117D /* Supporting Files */ = { - isa = PBXGroup; - children = ( - ); - name = "Supporting Files"; - sourceTree = ""; - }; - B70E9F531CE9FBE99B702E55 /* Frameworks */ = { - isa = PBXGroup; - children = ( - 0EF0191EA9C97779E7CB73A3 /* Pods_Runner.framework */, - ); - name = Frameworks; - sourceTree = ""; - }; -/* End PBXGroup section */ - -/* Begin PBXNativeTarget section */ - 97C146ED1CF9000F007C117D /* Runner */ = { - isa = PBXNativeTarget; - buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; - buildPhases = ( - D8BE761BB894E75EDBF8CFF4 /* [CP] Check Pods Manifest.lock */, - 9740EEB61CF901F6004384FC /* Run Script */, - 97C146EA1CF9000F007C117D /* Sources */, - 97C146EB1CF9000F007C117D /* Frameworks */, - 97C146EC1CF9000F007C117D /* Resources */, - 9705A1C41CF9048500538489 /* Embed Frameworks */, - 3B06AD1E1E4923F5004D2608 /* Thin Binary */, - EC7A9682D924C7840AB6C52F /* [CP] Embed Pods Frameworks */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = Runner; - productName = Runner; - productReference = 97C146EE1CF9000F007C117D /* Runner.app */; - productType = "com.apple.product-type.application"; - }; -/* End PBXNativeTarget section */ - -/* Begin PBXProject section */ - 97C146E61CF9000F007C117D /* Project object */ = { - isa = PBXProject; - attributes = { - LastUpgradeCheck = 1020; - ORGANIZATIONNAME = ""; - TargetAttributes = { - 97C146ED1CF9000F007C117D = { - CreatedOnToolsVersion = 7.3.1; - LastSwiftMigration = 1100; - }; - }; - }; - buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; - compatibilityVersion = "Xcode 9.3"; - developmentRegion = en; - hasScannedForEncodings = 0; - knownRegions = ( - en, - Base, - ); - mainGroup = 97C146E51CF9000F007C117D; - productRefGroup = 97C146EF1CF9000F007C117D /* Products */; - projectDirPath = ""; - projectRoot = ""; - targets = ( - 97C146ED1CF9000F007C117D /* Runner */, - ); - }; -/* End PBXProject section */ - -/* Begin PBXResourcesBuildPhase section */ - 97C146EC1CF9000F007C117D /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, - 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, - 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, - 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXResourcesBuildPhase section */ - -/* Begin PBXShellScriptBuildPhase section */ - 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - ); - name = "Thin Binary"; - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; - }; - 9740EEB61CF901F6004384FC /* Run Script */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - ); - name = "Run Script"; - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; - }; - D8BE761BB894E75EDBF8CFF4 /* [CP] Check Pods Manifest.lock */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; - outputFileListPaths = ( - ); - outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; - showEnvVarsInLog = 0; - }; - EC7A9682D924C7840AB6C52F /* [CP] Embed Pods Frameworks */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", - ); - name = "[CP] Embed Pods Frameworks"; - outputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; - showEnvVarsInLog = 0; - }; -/* End PBXShellScriptBuildPhase section */ - -/* Begin PBXSourcesBuildPhase section */ - 97C146EA1CF9000F007C117D /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */, - 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXSourcesBuildPhase section */ - -/* Begin PBXVariantGroup section */ - 97C146FA1CF9000F007C117D /* Main.storyboard */ = { - isa = PBXVariantGroup; - children = ( - 97C146FB1CF9000F007C117D /* Base */, - ); - name = Main.storyboard; - sourceTree = ""; - }; - 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { - isa = PBXVariantGroup; - children = ( - 97C147001CF9000F007C117D /* Base */, - ); - name = LaunchScreen.storyboard; - sourceTree = ""; - }; -/* End PBXVariantGroup section */ - -/* Begin XCBuildConfiguration section */ - 249021D3217E4FDB00AE95B9 /* Profile */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_NONNULL = YES; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_NO_COMMON_BLOCKS = YES; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; - MTL_ENABLE_DEBUG_INFO = NO; - SDKROOT = iphoneos; - SUPPORTED_PLATFORMS = iphoneos; - TARGETED_DEVICE_FAMILY = "1,2"; - VALIDATE_PRODUCT = YES; - }; - name = Profile; - }; - 249021D4217E4FDB00AE95B9 /* Profile */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CLANG_ENABLE_MODULES = YES; - CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; - DEVELOPMENT_TEAM = H33599VJ27; - ENABLE_BITCODE = NO; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/Flutter", - ); - INFOPLIST_FILE = Runner/Info.plist; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - ); - LIBRARY_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/Flutter", - ); - PRODUCT_BUNDLE_IDENTIFIER = dk.cachet.mobilityFeatures; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; - SWIFT_VERSION = 5.0; - VERSIONING_SYSTEM = "apple-generic"; - }; - name = Profile; - }; - 97C147031CF9000F007C117D /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_NONNULL = YES; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = dwarf; - ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_TESTABILITY = YES; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_DYNAMIC_NO_PIC = NO; - GCC_NO_COMMON_BLOCKS = YES; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - ); - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; - MTL_ENABLE_DEBUG_INFO = YES; - ONLY_ACTIVE_ARCH = YES; - SDKROOT = iphoneos; - TARGETED_DEVICE_FAMILY = "1,2"; - }; - name = Debug; - }; - 97C147041CF9000F007C117D /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_NONNULL = YES; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_NO_COMMON_BLOCKS = YES; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; - MTL_ENABLE_DEBUG_INFO = NO; - SDKROOT = iphoneos; - SUPPORTED_PLATFORMS = iphoneos; - SWIFT_COMPILATION_MODE = wholemodule; - SWIFT_OPTIMIZATION_LEVEL = "-O"; - TARGETED_DEVICE_FAMILY = "1,2"; - VALIDATE_PRODUCT = YES; - }; - name = Release; - }; - 97C147061CF9000F007C117D /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CLANG_ENABLE_MODULES = YES; - CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; - DEVELOPMENT_TEAM = H33599VJ27; - ENABLE_BITCODE = NO; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/Flutter", - ); - INFOPLIST_FILE = Runner/Info.plist; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - ); - LIBRARY_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/Flutter", - ); - PRODUCT_BUNDLE_IDENTIFIER = dk.cachet.mobilityFeatures; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 5.0; - VERSIONING_SYSTEM = "apple-generic"; - }; - name = Debug; - }; - 97C147071CF9000F007C117D /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CLANG_ENABLE_MODULES = YES; - CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; - DEVELOPMENT_TEAM = H33599VJ27; - ENABLE_BITCODE = NO; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/Flutter", - ); - INFOPLIST_FILE = Runner/Info.plist; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - ); - LIBRARY_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/Flutter", - ); - PRODUCT_BUNDLE_IDENTIFIER = dk.cachet.mobilityFeatures; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; - SWIFT_VERSION = 5.0; - VERSIONING_SYSTEM = "apple-generic"; - }; - name = Release; - }; -/* End XCBuildConfiguration section */ - -/* Begin XCConfigurationList section */ - 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 97C147031CF9000F007C117D /* Debug */, - 97C147041CF9000F007C117D /* Release */, - 249021D3217E4FDB00AE95B9 /* Profile */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 97C147061CF9000F007C117D /* Debug */, - 97C147071CF9000F007C117D /* Release */, - 249021D4217E4FDB00AE95B9 /* Profile */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; -/* End XCConfigurationList section */ - }; - rootObject = 97C146E61CF9000F007C117D /* Project object */; -} diff --git a/packages/mobility_features/mobility_features_example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/packages/mobility_features/mobility_features_example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata deleted file mode 100644 index 1d526a16e..000000000 --- a/packages/mobility_features/mobility_features_example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata +++ /dev/null @@ -1,7 +0,0 @@ - - - - - diff --git a/packages/mobility_features/mobility_features_example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/packages/mobility_features/mobility_features_example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist deleted file mode 100644 index 18d981003..000000000 --- a/packages/mobility_features/mobility_features_example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +++ /dev/null @@ -1,8 +0,0 @@ - - - - - IDEDidComputeMac32BitWarning - - - diff --git a/packages/mobility_features/mobility_features_example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/packages/mobility_features/mobility_features_example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings deleted file mode 100644 index f9b0d7c5e..000000000 --- a/packages/mobility_features/mobility_features_example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings +++ /dev/null @@ -1,8 +0,0 @@ - - - - - PreviewsEnabled - - - diff --git a/packages/mobility_features/mobility_features_example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/packages/mobility_features/mobility_features_example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme deleted file mode 100644 index a28140cfd..000000000 --- a/packages/mobility_features/mobility_features_example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ /dev/null @@ -1,91 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/packages/mobility_features/mobility_features_example/ios/Runner.xcworkspace/contents.xcworkspacedata b/packages/mobility_features/mobility_features_example/ios/Runner.xcworkspace/contents.xcworkspacedata deleted file mode 100644 index 21a3cc14c..000000000 --- a/packages/mobility_features/mobility_features_example/ios/Runner.xcworkspace/contents.xcworkspacedata +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - diff --git a/packages/mobility_features/mobility_features_example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/packages/mobility_features/mobility_features_example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist deleted file mode 100644 index 18d981003..000000000 --- a/packages/mobility_features/mobility_features_example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +++ /dev/null @@ -1,8 +0,0 @@ - - - - - IDEDidComputeMac32BitWarning - - - diff --git a/packages/mobility_features/mobility_features_example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/packages/mobility_features/mobility_features_example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings deleted file mode 100644 index f9b0d7c5e..000000000 --- a/packages/mobility_features/mobility_features_example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings +++ /dev/null @@ -1,8 +0,0 @@ - - - - - PreviewsEnabled - - - diff --git a/packages/mobility_features/mobility_features_example/ios/Runner/AppDelegate.swift b/packages/mobility_features/mobility_features_example/ios/Runner/AppDelegate.swift deleted file mode 100644 index 34cc7fa3b..000000000 --- a/packages/mobility_features/mobility_features_example/ios/Runner/AppDelegate.swift +++ /dev/null @@ -1,23 +0,0 @@ -import UIKit -import Flutter - -import background_locator - -func registerPlugins(registry: FlutterPluginRegistry) -> () { - if (!registry.hasPlugin("BackgroundLocatorPlugin")) { - GeneratedPluginRegistrant.register(with: registry) - } -} - - -@UIApplicationMain -@objc class AppDelegate: FlutterAppDelegate { - override func application( - _ application: UIApplication, - didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? - ) -> Bool { - GeneratedPluginRegistrant.register(with: self) - BackgroundLocatorPlugin.setPluginRegistrantCallback(registerPlugins) - return super.application(application, didFinishLaunchingWithOptions: launchOptions) - } -} diff --git a/packages/mobility_features/mobility_features_example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/packages/mobility_features/mobility_features_example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json deleted file mode 100644 index d36b1fab2..000000000 --- a/packages/mobility_features/mobility_features_example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json +++ /dev/null @@ -1,122 +0,0 @@ -{ - "images" : [ - { - "size" : "20x20", - "idiom" : "iphone", - "filename" : "Icon-App-20x20@2x.png", - "scale" : "2x" - }, - { - "size" : "20x20", - "idiom" : "iphone", - "filename" : "Icon-App-20x20@3x.png", - "scale" : "3x" - }, - { - "size" : "29x29", - "idiom" : "iphone", - "filename" : "Icon-App-29x29@1x.png", - "scale" : "1x" - }, - { - "size" : "29x29", - "idiom" : "iphone", - "filename" : "Icon-App-29x29@2x.png", - "scale" : "2x" - }, - { - "size" : "29x29", - "idiom" : "iphone", - "filename" : "Icon-App-29x29@3x.png", - "scale" : "3x" - }, - { - "size" : "40x40", - "idiom" : "iphone", - "filename" : "Icon-App-40x40@2x.png", - "scale" : "2x" - }, - { - "size" : "40x40", - "idiom" : "iphone", - "filename" : "Icon-App-40x40@3x.png", - "scale" : "3x" - }, - { - "size" : "60x60", - "idiom" : "iphone", - "filename" : "Icon-App-60x60@2x.png", - "scale" : "2x" - }, - { - "size" : "60x60", - "idiom" : "iphone", - "filename" : "Icon-App-60x60@3x.png", - "scale" : "3x" - }, - { - "size" : "20x20", - "idiom" : "ipad", - "filename" : "Icon-App-20x20@1x.png", - "scale" : "1x" - }, - { - "size" : "20x20", - "idiom" : "ipad", - "filename" : "Icon-App-20x20@2x.png", - "scale" : "2x" - }, - { - "size" : "29x29", - "idiom" : "ipad", - "filename" : "Icon-App-29x29@1x.png", - "scale" : "1x" - }, - { - "size" : "29x29", - "idiom" : "ipad", - "filename" : "Icon-App-29x29@2x.png", - "scale" : "2x" - }, - { - "size" : "40x40", - "idiom" : "ipad", - "filename" : "Icon-App-40x40@1x.png", - "scale" : "1x" - }, - { - "size" : "40x40", - "idiom" : "ipad", - "filename" : "Icon-App-40x40@2x.png", - "scale" : "2x" - }, - { - "size" : "76x76", - "idiom" : "ipad", - "filename" : "Icon-App-76x76@1x.png", - "scale" : "1x" - }, - { - "size" : "76x76", - "idiom" : "ipad", - "filename" : "Icon-App-76x76@2x.png", - "scale" : "2x" - }, - { - "size" : "83.5x83.5", - "idiom" : "ipad", - "filename" : "Icon-App-83.5x83.5@2x.png", - "scale" : "2x" - }, - { - "size" : "1024x1024", - "idiom" : "ios-marketing", - "filename" : "Icon-App-1024x1024@1x.png", - "scale" : "1x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} diff --git a/packages/mobility_features/mobility_features_example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png b/packages/mobility_features/mobility_features_example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png deleted file mode 100644 index dc9ada4725e9b0ddb1deab583e5b5102493aa332..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10932 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 diff --git a/packages/mobility_features/mobility_features_example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/packages/mobility_features/mobility_features_example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png deleted file mode 100644 index f091b6b0bca859a3f474b03065bef75ba58a9e4c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1588 zcmV-42Fv-0P)C1SqPt}wig>|5Crh^=oyX$BK<}M8eLU3e2hGT;=G|!_SP)7zNI6fqUMB=)y zRAZ>eDe#*r`yDAVgB_R*LB*MAc)8(b{g{9McCXW!lq7r(btRoB9!8B-#AI6JMb~YFBEvdsV)`mEQO^&#eRKx@b&x- z5lZm*!WfD8oCLzfHGz#u7sT0^VLMI1MqGxF^v+`4YYnVYgk*=kU?HsSz{v({E3lb9 z>+xILjBN)t6`=g~IBOelGQ(O990@BfXf(DRI5I$qN$0Gkz-FSc$3a+2fX$AedL4u{ z4V+5Ong(9LiGcIKW?_352sR;LtDPmPJXI{YtT=O8=76o9;*n%_m|xo!i>7$IrZ-{l z-x3`7M}qzHsPV@$v#>H-TpjDh2UE$9g6sysUREDy_R(a)>=eHw-WAyfIN z*qb!_hW>G)Tu8nSw9yn#3wFMiLcfc4pY0ek1}8(NqkBR@t4{~oC>ryc-h_ByH(Cg5 z>ao-}771+xE3um9lWAY1FeQFxowa1(!J(;Jg*wrg!=6FdRX+t_<%z&d&?|Bn){>zm zZQj(aA_HeBY&OC^jj*)N`8fa^ePOU72VpInJoI1?`ty#lvlNzs(&MZX+R%2xS~5Kh zX*|AU4QE#~SgPzOXe9>tRj>hjU@c1k5Y_mW*Jp3fI;)1&g3j|zDgC+}2Q_v%YfDax z!?umcN^n}KYQ|a$Lr+51Nf9dkkYFSjZZjkma$0KOj+;aQ&721~t7QUKx61J3(P4P1 zstI~7-wOACnWP4=8oGOwz%vNDqD8w&Q`qcNGGrbbf&0s9L0De{4{mRS?o0MU+nR_! zrvshUau0G^DeMhM_v{5BuLjb#Hh@r23lDAk8oF(C+P0rsBpv85EP>4CVMx#04MOfG z;P%vktHcXwTj~+IE(~px)3*MY77e}p#|c>TD?sMatC0Tu4iKKJ0(X8jxQY*gYtxsC z(zYC$g|@+I+kY;dg_dE>scBf&bP1Nc@Hz<3R)V`=AGkc;8CXqdi=B4l2k|g;2%#m& z*jfX^%b!A8#bI!j9-0Fi0bOXl(-c^AB9|nQaE`*)Hw+o&jS9@7&Gov#HbD~#d{twV zXd^Tr^mWLfFh$@Dr$e;PBEz4(-2q1FF0}c;~B5sA}+Q>TOoP+t>wf)V9Iy=5ruQa;z)y zI9C9*oUga6=hxw6QasLPnee@3^Rr*M{CdaL5=R41nLs(AHk_=Y+A9$2&H(B7!_pURs&8aNw7?`&Z&xY_Ye z)~D5Bog^td-^QbUtkTirdyK^mTHAOuptDflut!#^lnKqU md>ggs(5nOWAqO?umG&QVYK#ibz}*4>0000U6E9hRK9^#O7(mu>ETqrXGsduA8$)?`v2seloOCza43C{NQ$$gAOH**MCn0Q?+L7dl7qnbRdqZ8LSVp1ItDxhxD?t@5_yHg6A8yI zC*%Wgg22K|8E#!~cTNYR~@Y9KepMPrrB8cABapAFa=`H+UGhkXUZV1GnwR1*lPyZ;*K(i~2gp|@bzp8}og7e*#% zEnr|^CWdVV!-4*Y_7rFvlww2Ze+>j*!Z!pQ?2l->4q#nqRu9`ELo6RMS5=br47g_X zRw}P9a7RRYQ%2Vsd0Me{_(EggTnuN6j=-?uFS6j^u69elMypu?t>op*wBx<=Wx8?( ztpe^(fwM6jJX7M-l*k3kEpWOl_Vk3@(_w4oc}4YF4|Rt=2V^XU?#Yz`8(e?aZ@#li0n*=g^qOcVpd-Wbok=@b#Yw zqn8u9a)z>l(1kEaPYZ6hwubN6i<8QHgsu0oE) ziJ(p;Wxm>sf!K+cw>R-(^Y2_bahB+&KI9y^);#0qt}t-$C|Bo71lHi{_+lg#f%RFy z0um=e3$K3i6K{U_4K!EX?F&rExl^W|G8Z8;`5z-k}OGNZ0#WVb$WCpQu-_YsiqKP?BB# vzVHS-CTUF4Ozn5G+mq_~Qqto~ahA+K`|lyv3(-e}00000NkvXXu0mjfd`9t{ diff --git a/packages/mobility_features/mobility_features_example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/packages/mobility_features/mobility_features_example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png deleted file mode 100644 index d0ef06e7edb86cdfe0d15b4b0d98334a86163658..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1716 zcmds$`#;kQ7{|XelZftyR5~xW7?MLxS4^|Hw3&P7^y)@A9Fj{Xm1~_CIV^XZ%SLBn zA;!r`GqGHg=7>xrB{?psZQs88ZaedDoagm^KF{a*>G|dJWRSe^I$DNW008I^+;Kjt z>9p3GNR^I;v>5_`+91i(*G;u5|L+Bu6M=(afLjtkya#yZ175|z$pU~>2#^Z_pCZ7o z1c6UNcv2B3?; zX%qdxCXQpdKRz=#b*q0P%b&o)5ZrNZt7$fiETSK_VaY=mb4GK`#~0K#~9^ zcY!`#Af+4h?UMR-gMKOmpuYeN5P*RKF!(tb`)oe0j2BH1l?=>y#S5pMqkx6i{*=V9JF%>N8`ewGhRE(|WohnD59R^$_36{4>S zDFlPC5|k?;SPsDo87!B{6*7eqmMdU|QZ84>6)Kd9wNfh90=y=TFQay-0__>=<4pk& zYDjgIhL-jQ9o>z32K)BgAH+HxamL{ZL~ozu)Qqe@a`FpH=oQRA8=L-m-1dam(Ix2V z?du;LdMO+ooBelr^_y4{|44tmgH^2hSzPFd;U^!1p>6d|o)(-01z{i&Kj@)z-yfWQ)V#3Uo!_U}q3u`(fOs`_f^ueFii1xBNUB z6MecwJN$CqV&vhc+)b(p4NzGGEgwWNs z@*lUV6LaduZH)4_g!cE<2G6#+hJrWd5(|p1Z;YJ7ifVHv+n49btR}dq?HHDjl{m$T z!jLZcGkb&XS2OG~u%&R$(X+Z`CWec%QKt>NGYvd5g20)PU(dOn^7%@6kQb}C(%=vr z{?RP(z~C9DPnL{q^@pVw@|Vx~@3v!9dCaBtbh2EdtoNHm4kGxp>i#ct)7p|$QJs+U z-a3qtcPvhihub?wnJqEt>zC@)2suY?%-96cYCm$Q8R%-8$PZYsx3~QOLMDf(piXMm zB=<63yQk1AdOz#-qsEDX>>c)EES%$owHKue;?B3)8aRd}m~_)>SL3h2(9X;|+2#7X z+#2)NpD%qJvCQ0a-uzZLmz*ms+l*N}w)3LRQ*6>|Ub-fyptY(keUxw+)jfwF5K{L9 z|Cl_w=`!l_o><384d&?)$6Nh(GAm=4p_;{qVn#hI8lqewW7~wUlyBM-4Z|)cZr?Rh z=xZ&Ol>4(CU85ea(CZ^aO@2N18K>ftl8>2MqetAR53_JA>Fal`^)1Y--Am~UDa4th zKfCYpcXky$XSFDWBMIl(q=Mxj$iMBX=|j9P)^fDmF(5(5$|?Cx}DKEJa&XZP%OyE`*GvvYQ4PV&!g2|L^Q z?YG}tx;sY@GzMmsY`7r$P+F_YLz)(e}% zyakqFB<6|x9R#TdoP{R$>o7y(-`$$p0NxJ6?2B8tH)4^yF(WhqGZlM3=9Ibs$%U1w zWzcss*_c0=v_+^bfb`kBFsI`d;ElwiU%frgRB%qBjn@!0U2zZehBn|{%uNIKBA7n= zzE`nnwTP85{g;8AkYxA68>#muXa!G>xH22D1I*SiD~7C?7Za+9y7j1SHiuSkKK*^O zsZ==KO(Ua#?YUpXl{ViynyT#Hzk=}5X$e04O@fsMQjb}EMuPWFO0e&8(2N(29$@Vd zn1h8Yd>6z(*p^E{c(L0Lg=wVdupg!z@WG;E0k|4a%s7Up5C0c)55XVK*|x9RQeZ1J@1v9MX;>n34(i>=YE@Iur`0Vah(inE3VUFZNqf~tSz{1fz3Fsn_x4F>o(Yo;kpqvBe-sbwH(*Y zu$JOl0b83zu$JMvy<#oH^Wl>aWL*?aDwnS0iEAwC?DK@aT)GHRLhnz2WCvf3Ba;o=aY7 z2{Asu5MEjGOY4O#Ggz@@J;q*0`kd2n8I3BeNuMmYZf{}pg=jTdTCrIIYuW~luKecn z+E-pHY%ohj@uS0%^ z&(OxwPFPD$+#~`H?fMvi9geVLci(`K?Kj|w{rZ9JgthFHV+=6vMbK~0)Ea<&WY-NC zy-PnZft_k2tfeQ*SuC=nUj4H%SQ&Y$gbH4#2sT0cU0SdFs=*W*4hKGpuR1{)mV;Qf5pw4? zfiQgy0w3fC*w&Bj#{&=7033qFR*<*61B4f9K%CQvxEn&bsWJ{&winp;FP!KBj=(P6 z4Z_n4L7cS;ao2)ax?Tm|I1pH|uLpDSRVghkA_UtFFuZ0b2#>!8;>-_0ELjQSD-DRd z4im;599VHDZYtnWZGAB25W-e(2VrzEh|etsv2YoP#VbIZ{aFkwPrzJ#JvCvA*mXS& z`}Q^v9(W4GiSs}#s7BaN!WA2bniM$0J(#;MR>uIJ^uvgD3GS^%*ikdW6-!VFUU?JV zZc2)4cMsX@j z5HQ^e3BUzOdm}yC-xA%SY``k$rbfk z;CHqifhU*jfGM@DkYCecD9vl*qr58l6x<8URB=&%{!Cu3RO*MrKZ4VO}V6R0a zZw3Eg^0iKWM1dcTYZ0>N899=r6?+adUiBKPciJw}L$=1f4cs^bio&cr9baLF>6#BM z(F}EXe-`F=f_@`A7+Q&|QaZ??Txp_dB#lg!NH=t3$G8&06MFhwR=Iu*Im0s_b2B@| znW>X}sy~m#EW)&6E&!*0%}8UAS)wjt+A(io#wGI@Z2S+Ms1Cxl%YVE800007ip7{`C_J2TxPmfw%h$|%acrYHt)Re^PB%O&&=~a zhS(%I#+V>J-vjIib^<+s%ludY7y^C(P8nmqn9fp!i+?vr`bziDE=bx`%2W#Xyrj|i z!XQ4v1%L`m{7KT7q+LZNB^h8Ha2e=`Wp65^0;J00)_^G=au=8Yo;1b`CV&@#=jIBo zjN^JNVfYSs)+kDdGe7`1&8!?MQYKS?DuHZf3iogk_%#9E|5S zWeHrmAo>P;ejX7mwq#*}W25m^ZI+{(Z8fI?4jM_fffY0nok=+88^|*_DwcW>mR#e+ zX$F_KMdb6sRz!~7KkyN0G(3XQ+;z3X%PZ4gh;n-%62U<*VUKNv(D&Q->Na@Xb&u5Q3`3DGf+a8O5x7c#7+R+EAYl@R5us)CIw z7sT@_y~Ao@uL#&^LIh&QceqiT^+lb0YbFZt_SHOtWA%mgPEKVNvVgCsXy{5+zl*X8 zCJe)Q@y>wH^>l4;h1l^Y*9%-23TSmE>q5nI@?mt%n;Sj4Qq`Z+ib)a*a^cJc%E9^J zB;4s+K@rARbcBLT5P=@r;IVnBMKvT*)ew*R;&8vu%?Z&S>s?8?)3*YawM0P4!q$Kv zMmKh3lgE~&w&v%wVzH3Oe=jeNT=n@Y6J6TdHWTjXfX~-=1A1Bw`EW8rn}MqeI34nh zexFeA?&C3B2(E?0{drE@DA2pu(A#ElY&6el60Rn|Qpn-FkfQ8M93AfWIr)drgDFEU zghdWK)^71EWCP(@(=c4kfH1Y(4iugD4fve6;nSUpLT%!)MUHs1!zJYy4y||C+SwQ! z)KM&$7_tyM`sljP2fz6&Z;jxRn{Wup8IOUx8D4uh&(=O zx-7$a;U><*5L^!%xRlw)vAbh;sdlR||& ze}8_8%)c2Fwy=F&H|LM+p{pZB5DKTx>Y?F1N%BlZkXf!}JeGuMZk~LPi7{cidvUGB zAJ4LVeNV%XO>LTrklB#^-;8nb;}6l;1oW&WS=Mz*Az!4cqqQzbOSFq`$Q%PfD7srM zpKgP-D_0XPTRX*hAqeq0TDkJ;5HB1%$3Np)99#16c{ zJImlNL(npL!W|Gr_kxl1GVmF5&^$^YherS7+~q$p zt}{a=*RiD2Ikv6o=IM1kgc7zqpaZ;OB)P!1zz*i3{U()Dq#jG)egvK}@uFLa`oyWZ zf~=MV)|yJn`M^$N%ul5);JuQvaU1r2wt(}J_Qgyy`qWQI`hEeRX0uC@c1(dQ2}=U$ tNIIaX+dr)NRWXcxoR{>fqI{SF_dm1Ylv~=3YHI)h002ovPDHLkV1g(pWS;;4 diff --git a/packages/mobility_features/mobility_features_example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/packages/mobility_features/mobility_features_example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png deleted file mode 100644 index c8f9ed8f5cee1c98386d13b17e89f719e83555b2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1895 zcmV-t2blPYP)FQtfgmafE#=YDCq`qUBt#QpG%*H6QHY765~R=q zZ6iudfM}q!Pz#~9JgOi8QJ|DSu?1-*(kSi1K4#~5?#|rh?sS)(-JQqX*}ciXJ56_H zdw=^s_srbAdqxlvGyrgGet#6T7_|j;95sL%MtM;q86vOxKM$f#puR)Bjv9Zvz9-di zXOTSsZkM83)E9PYBXC<$6(|>lNLVBb&&6y{NByFCp%6+^ALR@NCTse_wqvNmSWI-m z!$%KlHFH2omF!>#%1l3LTZg(s7eof$7*xB)ZQ0h?ejh?Ta9fDv59+u#MokW+1t8Zb zgHv%K(u9G^Lv`lh#f3<6!JVTL3(dCpxHbnbA;kKqQyd1~^Xe0VIaYBSWm6nsr;dFj z4;G-RyL?cYgsN1{L4ZFFNa;8)Rv0fM0C(~Tkit94 zz#~A)59?QjD&pAPSEQ)p8gP|DS{ng)j=2ux)_EzzJ773GmQ_Cic%3JJhC0t2cx>|v zJcVusIB!%F90{+}8hG3QU4KNeKmK%T>mN57NnCZ^56=0?&3@!j>a>B43pi{!u z7JyDj7`6d)qVp^R=%j>UIY6f+3`+qzIc!Y_=+uN^3BYV|o+$vGo-j-Wm<10%A=(Yk^beI{t%ld@yhKjq0iNjqN4XMGgQtbKubPM$JWBz}YA65k%dm*awtC^+f;a-x4+ddbH^7iDWGg&N0n#MW{kA|=8iMUiFYvMoDY@sPC#t$55gn6ykUTPAr`a@!(;np824>2xJthS z*ZdmT`g5-`BuJs`0LVhz+D9NNa3<=6m;cQLaF?tCv8)zcRSh66*Z|vXhG@$I%U~2l z?`Q zykI#*+rQ=z6Jm=Bui-SfpDYLA=|vzGE(dYm=OC8XM&MDo7ux4UF1~0J1+i%aCUpRe zt3L_uNyQ*cE(38Uy03H%I*)*Bh=Lb^Xj3?I^Hnbeq72(EOK^Y93CNp*uAA{5Lc=ky zx=~RKa4{iTm{_>_vSCm?$Ej=i6@=m%@VvAITnigVg{&@!7CDgs908761meDK5azA} z4?=NOH|PdvabgJ&fW2{Mo$Q0CcD8Qc84%{JPYt5EiG{MdLIAeX%T=D7NIP4%Hw}p9 zg)==!2Lbp#j{u_}hMiao9=!VSyx0gHbeCS`;q&vzeq|fs`y&^X-lso(Ls@-706qmA z7u*T5PMo_w3{se1t2`zWeO^hOvTsohG_;>J0wVqVe+n)AbQCx)yh9;w+J6?NF5Lmo zecS@ieAKL8%bVd@+-KT{yI|S}O>pYckUFs;ry9Ow$CD@ztz5K-*D$^{i(_1llhSh^ zEkL$}tsQt5>QA^;QgjgIfBDmcOgi5YDyu?t6vSnbp=1+@6D& z5MJ}B8q;bRlVoxasyhcUF1+)o`&3r0colr}QJ3hcSdLu;9;td>kf@Tcn<@9sIx&=m z;AD;SCh95=&p;$r{Xz3iWCO^MX83AGJ(yH&eTXgv|0=34#-&WAmw{)U7OU9!Wz^!7 zZ%jZFi@JR;>Mhi7S>V7wQ176|FdW2m?&`qa(ScO^CFPR80HucLHOTy%5s*HR0^8)i h0WYBP*#0Ks^FNSabJA*5${_#%002ovPDHLkV1oKhTl@e3 diff --git a/packages/mobility_features/mobility_features_example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png b/packages/mobility_features/mobility_features_example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png deleted file mode 100644 index a6d6b8609df07bf62e5100a53a01510388bd2b22..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2665 zcmV-v3YPVWP)oFh3q0MFesq&64WThn3$;G69TfjsAv=f2G9}p zgSx99+!YV6qME!>9MD13x)k(+XE7W?_O4LoLb5ND8 zaV{9+P@>42xDfRiYBMSgD$0!vssptcb;&?u9u(LLBKmkZ>RMD=kvD3h`sk6!QYtBa ztlZI#nu$8lJ^q2Z79UTgZe>BU73(Aospiq+?SdMt8lDZ;*?@tyWVZVS_Q7S&*tJaiRlJ z+aSMOmbg3@h5}v;A*c8SbqM3icg-`Cnwl;7Ts%A1RkNIp+Txl-Ckkvg4oxrqGA5ewEgYqwtECD<_3Egu)xGllKt&J8g&+=ac@Jq4-?w6M3b*>w5 z69N3O%=I^6&UL5gZ!}trC7bUj*12xLdkNs~Bz4QdJJ*UDZox2UGR}SNg@lmOvhCc~ z*f_UeXv(=#I#*7>VZx2ObEN~UoGUTl=-@)E;YtCRZ>SVp$p9yG5hEFZ!`wI!spd)n zSk+vK0Vin7FL{7f&6OB%f;SH22dtbcF<|9fi2Fp%q4kxL!b1#l^)8dUwJ zwEf{(wJj@8iYDVnKB`eSU+;ml-t2`@%_)0jDM`+a46xhDbBj2+&Ih>1A>6aky#(-SYyE{R3f#y57wfLs z6w1p~$bp;6!9DX$M+J~S@D6vJAaElETnsX4h9a5tvPhC3L@qB~bOzkL@^z0k_hS{T4PF*TDrgdXp+dzsE? z>V|VR035Pl9n5&-RePFdS{7KAr2vPOqR9=M$vXA1Yy5>w;EsF`;OK{2pkn-kpp9Pw z)r;5JfJKKaT$4qCb{TaXHjb$QA{y0EYy*+b1XI;6Ah- zw13P)xT`>~eFoJC!>{2XL(a_#upp3gaR1#5+L(Jmzp4TBnx{~WHedpJ1ch8JFk~Sw z>F+gN+i+VD?gMXwcIhn8rz`>e>J^TI3E-MW>f}6R-pL}>WMOa0k#jN+`RyUVUC;#D zg|~oS^$6%wpF{^Qr+}X>0PKcr3Fc&>Z>uv@C);pwDs@2bZWhYP!rvGx?_|q{d`t<*XEb#=aOb=N+L@CVBGqImZf&+a zCQEa3$~@#kC);pasdG=f6tuIi0PO-y&tvX%>Mv=oY3U$nD zJ#gMegnQ46pq+3r=;zmgcG+zRc9D~c>z+jo9&D+`E6$LmyFqlmCYw;-Zooma{sR@~ z)_^|YL1&&@|GXo*pivH7k!msl+$Sew3%XJnxajt0K%3M6Bd&YFNy9}tWG^aovK2eX z1aL1%7;KRDrA@eG-Wr6w+;*H_VD~qLiVI`{_;>o)k`{8xa3EJT1O_>#iy_?va0eR? zDV=N%;Zjb%Z2s$@O>w@iqt!I}tLjGk!=p`D23I}N4Be@$(|iSA zf3Ih7b<{zqpDB4WF_5X1(peKe+rASze%u8eKLn#KKXt;UZ+Adf$_TO+vTqshLLJ5c z52HucO=lrNVae5XWOLm!V@n-ObU11!b+DN<$RuU+YsrBq*lYT;?AwJpmNKniF0Q1< zJCo>Q$=v$@&y=sj6{r!Y&y&`0$-I}S!H_~pI&2H8Z1C|BX4VgZ^-! zje3-;x0PBD!M`v*J_)rL^+$<1VJhH*2Fi~aA7s&@_rUHYJ9zD=M%4AFQ`}k8OC$9s XsPq=LnkwKG00000NkvXXu0mjfhAk5^ diff --git a/packages/mobility_features/mobility_features_example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/packages/mobility_features/mobility_features_example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png deleted file mode 100644 index a6d6b8609df07bf62e5100a53a01510388bd2b22..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2665 zcmV-v3YPVWP)oFh3q0MFesq&64WThn3$;G69TfjsAv=f2G9}p zgSx99+!YV6qME!>9MD13x)k(+XE7W?_O4LoLb5ND8 zaV{9+P@>42xDfRiYBMSgD$0!vssptcb;&?u9u(LLBKmkZ>RMD=kvD3h`sk6!QYtBa ztlZI#nu$8lJ^q2Z79UTgZe>BU73(Aospiq+?SdMt8lDZ;*?@tyWVZVS_Q7S&*tJaiRlJ z+aSMOmbg3@h5}v;A*c8SbqM3icg-`Cnwl;7Ts%A1RkNIp+Txl-Ckkvg4oxrqGA5ewEgYqwtECD<_3Egu)xGllKt&J8g&+=ac@Jq4-?w6M3b*>w5 z69N3O%=I^6&UL5gZ!}trC7bUj*12xLdkNs~Bz4QdJJ*UDZox2UGR}SNg@lmOvhCc~ z*f_UeXv(=#I#*7>VZx2ObEN~UoGUTl=-@)E;YtCRZ>SVp$p9yG5hEFZ!`wI!spd)n zSk+vK0Vin7FL{7f&6OB%f;SH22dtbcF<|9fi2Fp%q4kxL!b1#l^)8dUwJ zwEf{(wJj@8iYDVnKB`eSU+;ml-t2`@%_)0jDM`+a46xhDbBj2+&Ih>1A>6aky#(-SYyE{R3f#y57wfLs z6w1p~$bp;6!9DX$M+J~S@D6vJAaElETnsX4h9a5tvPhC3L@qB~bOzkL@^z0k_hS{T4PF*TDrgdXp+dzsE? z>V|VR035Pl9n5&-RePFdS{7KAr2vPOqR9=M$vXA1Yy5>w;EsF`;OK{2pkn-kpp9Pw z)r;5JfJKKaT$4qCb{TaXHjb$QA{y0EYy*+b1XI;6Ah- zw13P)xT`>~eFoJC!>{2XL(a_#upp3gaR1#5+L(Jmzp4TBnx{~WHedpJ1ch8JFk~Sw z>F+gN+i+VD?gMXwcIhn8rz`>e>J^TI3E-MW>f}6R-pL}>WMOa0k#jN+`RyUVUC;#D zg|~oS^$6%wpF{^Qr+}X>0PKcr3Fc&>Z>uv@C);pwDs@2bZWhYP!rvGx?_|q{d`t<*XEb#=aOb=N+L@CVBGqImZf&+a zCQEa3$~@#kC);pasdG=f6tuIi0PO-y&tvX%>Mv=oY3U$nD zJ#gMegnQ46pq+3r=;zmgcG+zRc9D~c>z+jo9&D+`E6$LmyFqlmCYw;-Zooma{sR@~ z)_^|YL1&&@|GXo*pivH7k!msl+$Sew3%XJnxajt0K%3M6Bd&YFNy9}tWG^aovK2eX z1aL1%7;KRDrA@eG-Wr6w+;*H_VD~qLiVI`{_;>o)k`{8xa3EJT1O_>#iy_?va0eR? zDV=N%;Zjb%Z2s$@O>w@iqt!I}tLjGk!=p`D23I}N4Be@$(|iSA zf3Ih7b<{zqpDB4WF_5X1(peKe+rASze%u8eKLn#KKXt;UZ+Adf$_TO+vTqshLLJ5c z52HucO=lrNVae5XWOLm!V@n-ObU11!b+DN<$RuU+YsrBq*lYT;?AwJpmNKniF0Q1< zJCo>Q$=v$@&y=sj6{r!Y&y&`0$-I}S!H_~pI&2H8Z1C|BX4VgZ^-! zje3-;x0PBD!M`v*J_)rL^+$<1VJhH*2Fi~aA7s&@_rUHYJ9zD=M%4AFQ`}k8OC$9s XsPq=LnkwKG00000NkvXXu0mjfhAk5^ diff --git a/packages/mobility_features/mobility_features_example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/packages/mobility_features/mobility_features_example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png deleted file mode 100644 index 75b2d164a5a98e212cca15ea7bf2ab5de5108680..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3831 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 diff --git a/packages/mobility_features/mobility_features_example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png b/packages/mobility_features/mobility_features_example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png deleted file mode 100644 index c4df70d39da7941ef3f6dcb7f06a192d8dcb308d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1888 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 iW8Zci7-kcv6Uzs@r-FtIZ-&5|)J Q1PU{Fy85}Sb4q9e0B4a5jsO4v diff --git a/packages/mobility_features/mobility_features_example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png b/packages/mobility_features/mobility_features_example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png deleted file mode 100644 index 9da19eacad3b03bb08bbddbbf4ac48dd78b3d838..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 68 zcmeAS@N?(olHy`uVBq!ia0vp^j3CUx0wlM}@Gt=>Zci7-kcv6Uzs@r-FtIZ-&5|)J Q1PU{Fy85}Sb4q9e0B4a5jsO4v diff --git a/packages/mobility_features/mobility_features_example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png b/packages/mobility_features/mobility_features_example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png deleted file mode 100644 index 9da19eacad3b03bb08bbddbbf4ac48dd78b3d838..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 68 zcmeAS@N?(olHy`uVBq!ia0vp^j3CUx0wlM}@Gt=>Zci7-kcv6Uzs@r-FtIZ-&5|)J Q1PU{Fy85}Sb4q9e0B4a5jsO4v diff --git a/packages/mobility_features/mobility_features_example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md b/packages/mobility_features/mobility_features_example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md deleted file mode 100644 index 89c2725b7..000000000 --- a/packages/mobility_features/mobility_features_example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md +++ /dev/null @@ -1,5 +0,0 @@ -# Launch Screen Assets - -You can customize the launch screen with your own desired assets by replacing the image files in this directory. - -You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. \ No newline at end of file diff --git a/packages/mobility_features/mobility_features_example/ios/Runner/Base.lproj/LaunchScreen.storyboard b/packages/mobility_features/mobility_features_example/ios/Runner/Base.lproj/LaunchScreen.storyboard deleted file mode 100644 index f2e259c7c..000000000 --- a/packages/mobility_features/mobility_features_example/ios/Runner/Base.lproj/LaunchScreen.storyboard +++ /dev/null @@ -1,37 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/packages/mobility_features/mobility_features_example/ios/Runner/Base.lproj/Main.storyboard b/packages/mobility_features/mobility_features_example/ios/Runner/Base.lproj/Main.storyboard deleted file mode 100644 index f3c28516f..000000000 --- a/packages/mobility_features/mobility_features_example/ios/Runner/Base.lproj/Main.storyboard +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/packages/mobility_features/mobility_features_example/ios/Runner/Info.plist b/packages/mobility_features/mobility_features_example/ios/Runner/Info.plist deleted file mode 100644 index cb13bd74d..000000000 --- a/packages/mobility_features/mobility_features_example/ios/Runner/Info.plist +++ /dev/null @@ -1,55 +0,0 @@ - - - - - CFBundleDevelopmentRegion - $(DEVELOPMENT_LANGUAGE) - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - mobility_features_example - CFBundlePackageType - APPL - CFBundleShortVersionString - $(FLUTTER_BUILD_NAME) - CFBundleSignature - ???? - CFBundleVersion - $(FLUTTER_BUILD_NUMBER) - LSRequiresIPhoneOS - - NSLocationAlwaysAndWhenInUseUsageDescription - This app needs access to location when open and in the background. - NSLocationAlwaysUsageDescription - This app needs access to location when in the background. - NSLocationWhenInUseUsageDescription - This app needs access to location when open. - UIBackgroundModes - - location - - UILaunchStoryboardName - LaunchScreen - UIMainStoryboardFile - Main - UISupportedInterfaceOrientations - - UIInterfaceOrientationPortrait - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - UISupportedInterfaceOrientations~ipad - - UIInterfaceOrientationPortrait - UIInterfaceOrientationPortraitUpsideDown - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - UIViewControllerBasedStatusBarAppearance - - - diff --git a/packages/mobility_features/mobility_features_example/ios/Runner/Runner-Bridging-Header.h b/packages/mobility_features/mobility_features_example/ios/Runner/Runner-Bridging-Header.h deleted file mode 100644 index 308a2a560..000000000 --- a/packages/mobility_features/mobility_features_example/ios/Runner/Runner-Bridging-Header.h +++ /dev/null @@ -1 +0,0 @@ -#import "GeneratedPluginRegistrant.h" diff --git a/packages/mobility_features/mobility_features_example/lib/main.dart b/packages/mobility_features/mobility_features_example/lib/main.dart deleted file mode 100644 index 7d6a98e77..000000000 --- a/packages/mobility_features/mobility_features_example/lib/main.dart +++ /dev/null @@ -1,273 +0,0 @@ -library mobility_app; - -import 'dart:async'; -import 'package:flutter/material.dart'; - -import 'package:carp_background_location/carp_background_location.dart'; -import 'package:mobility_features/mobility_features.dart'; - -part 'stops_page.dart'; - -part 'moves_page.dart'; - -part 'places_page.dart'; - -void main() => runApp(MyApp()); - -Widget entry(String key, String value, Icon icon) { - return Container( - padding: const EdgeInsets.all(2), - margin: EdgeInsets.all(3), - child: ListTile( - leading: icon, - title: Text(key), - trailing: Text(value), - )); -} - -String formatDate(DateTime date) { - return '${date.year}/${date.month}/${date.day}'; -} - -String interval(DateTime a, DateTime b) { - String pad(int x) => '${x.toString().padLeft(2, '0')}'; - return '${pad(a.hour)}:${pad(a.minute)}:${pad(a.second)} - ${pad(b.hour)}:${pad(b.minute)}:${pad(b.second)}'; -} - -String formatDuration(Duration duration) { - String twoDigits(int n) => n.toString().padLeft(2, "0"); - String twoDigitMinutes = twoDigits(duration.inMinutes.remainder(60)); - String twoDigitSeconds = twoDigits(duration.inSeconds.remainder(60)); - return "${twoDigits(duration.inHours)}:$twoDigitMinutes:$twoDigitSeconds"; -} - -final stopIcon = Icon(Icons.my_location); -final moveIcon = Icon(Icons.directions_walk); -final placeIcon = Icon(Icons.place); -final featuresIcon = Icon(Icons.assessment); -final homeStayIcon = Icon(Icons.home); -final distanceTravelledIcon = Icon(Icons.card_travel); -final entropyIcon = Icon(Icons.equalizer); -final varianceIcon = Icon(Icons.swap_calls); - -enum AppState { NO_FEATURES, CALCULATING_FEATURES, FEATURES_READY } - -class MyApp extends StatelessWidget { - // This widget is the root of your application. - @override - Widget build(BuildContext context) { - return MaterialApp( - title: 'Flutter Demo', - theme: ThemeData( - primarySwatch: Colors.blue, - ), - home: MyHomePage(title: 'Mobility Features Demo'), - ); - } -} - -String dtoToString(LocationDto dto) => - '${dto.latitude}, ${dto.longitude} @ ${DateTime.fromMillisecondsSinceEpoch(dto.time ~/ 1)}'; - -class MyHomePage extends StatefulWidget { - MyHomePage({Key key, this.title}) : super(key: key); - final String title; - - @override - _MyHomePageState createState() => _MyHomePageState(); -} - -class _MyHomePageState extends State { - AppState _state = AppState.NO_FEATURES; - - int _currentIndex = 0; - - /// Location Streaming - LocationManager locationManager = LocationManager.instance; - Stream dtoStream; - StreamSubscription dtoSubscription; - - /// Mobility Features stream - StreamSubscription mobilitySubscription; - MobilityContext _mobilityContext; - - @override - void initState() { - super.initState(); - - /// Set up Mobility Features - MobilityFeatures().stopDuration = Duration(seconds: 20); - MobilityFeatures().placeRadius = 50.0; - MobilityFeatures().stopRadius = 5.0; - - /// Setup Location Manager - locationManager.distanceFilter = 0; - locationManager.interval = 1; - locationManager.notificationTitle = 'Mobility Features'; - locationManager.notificationMsg = 'Your geo-location is being tracked'; - streamInit(); - } - - @override - void dispose() { - mobilitySubscription?.cancel(); - super.dispose(); - } - - void onMobilityContext(MobilityContext context) { - print('Context received: ${context.toJson()}'); - setState(() { - _state = AppState.FEATURES_READY; - _mobilityContext = context; - }); - } - - void streamInit() async { - /// Set up streams: - /// * Subscribe to stream in case it is already running (Android only) - /// * Subscribe to MobilityContext updates - dtoStream = locationManager.dtoStream; - dtoSubscription = dtoStream.listen(onData); - - // Subscribe if it hasn't been done already - if (dtoSubscription != null) { - dtoSubscription.cancel(); - } - dtoSubscription = dtoStream.listen(onData); - await locationManager.start(); - - Stream locationSampleStream = dtoStream.map((e) => - LocationSample(GeoLocation(e.latitude, e.longitude), DateTime.now())); - - MobilityFeatures().startListening(locationSampleStream); - mobilitySubscription = - MobilityFeatures().contextStream.listen(onMobilityContext); - } - - void onData(LocationDto dto) { - print(dtoToString(dto)); - } - - Widget get featuresOverview { - return ListView( - children: [ - entry("Stops", "${_mobilityContext.stops.length}", stopIcon), - entry("Moves", "${_mobilityContext.moves.length}", moveIcon), - entry("Significant Places", - "${_mobilityContext.numberOfSignificantPlaces}", placeIcon), - entry( - "Home Stay", - _mobilityContext.homeStay < 0 - ? "?" - : "${(_mobilityContext.homeStay * 100).toStringAsFixed(1)}%", - homeStayIcon), - entry( - "Distance Travelled", - "${(_mobilityContext.distanceTravelled / 1000).toStringAsFixed(2)} km", - distanceTravelledIcon), - entry( - "Normalized Entropy", - "${_mobilityContext.normalizedEntropy.toStringAsFixed(2)}", - entropyIcon), - entry( - "Location Variance", - "${(111.133 * _mobilityContext.locationVariance).toStringAsFixed(5)} km", - varianceIcon), - ], - ); - } - - List get contentNoFeatures { - return [ - Container( - margin: EdgeInsets.all(25), - child: Text( - 'Move around to start generating features', - style: TextStyle(fontSize: 20), - )) - ]; - } - - List get contentFeaturesReady { - return [ - Container( - margin: EdgeInsets.all(25), - child: Column(children: [ - Text( - 'Statistics for today,', - style: TextStyle(fontSize: 20), - ), - Text( - '${formatDate(_mobilityContext.date)}', - style: TextStyle(fontSize: 20, color: Colors.blue), - ), - ])), - Expanded(child: featuresOverview), - ]; - } - - Widget get content { - List children; - if (_state == AppState.FEATURES_READY) - children = contentFeaturesReady; - else - children = contentNoFeatures; - return Column(children: children); - } - - void onTabTapped(int index) { - setState(() { - _currentIndex = index; - }); - } - - Widget _navBar() { - return BottomNavigationBar( - onTap: onTabTapped, // new - currentIndex: _currentIndex, // this will be set when a new tab is tapped - type: BottomNavigationBarType.fixed, - items: [ - BottomNavigationBarItem(icon: featuresIcon, label: 'Features'), - BottomNavigationBarItem(icon: stopIcon, label: 'Stops'), - BottomNavigationBarItem(icon: placeIcon, label: 'Places'), - BottomNavigationBarItem(icon: moveIcon, label: 'Moves') - ], - ); - } - - @override - Widget build(BuildContext context) { - List stops = []; - List moves = []; - List places = []; - - if (_mobilityContext != null) { - for (var x in _mobilityContext.stops) print(x); - for (var x in _mobilityContext.moves) { - print(x); - print('${x.stopFrom} --> ${x.stopTo}'); - } - stops = _mobilityContext.stops; - moves = _mobilityContext.moves; - places = _mobilityContext.places; - } - - List pages = [ - content, - StopsPage(stops), - PlacesPage(places), - MovesPage(moves), - ]; - - return Scaffold( - appBar: AppBar( - backgroundColor: Colors.teal, - // Here we take the value from the MyHomePage object that was created by - // the App.build method, and use it to set our appbar title. - title: Text(widget.title), - ), - body: pages[_currentIndex], - bottomNavigationBar: _navBar(), - ); - } -} diff --git a/packages/mobility_features/mobility_features_example/lib/moves_page.dart b/packages/mobility_features/mobility_features_example/lib/moves_page.dart deleted file mode 100644 index b6486a73a..000000000 --- a/packages/mobility_features/mobility_features_example/lib/moves_page.dart +++ /dev/null @@ -1,31 +0,0 @@ -part of mobility_app; - -class MovesPage extends StatelessWidget { - final List moves; - - MovesPage(this.moves); - - Widget moveEntry(Move m) { - return Container( - padding: const EdgeInsets.all(2), - margin: EdgeInsets.all(3), - child: ListTile( - leading: Text('Place ${m.stopFrom.placeId} → ${m.stopTo.placeId}'), - title: Text('${m.distance.toInt()} meters'), - trailing: Text('${formatDuration(m.duration)}'), - )); - } - - Widget list() { - return ListView.builder( - itemCount: moves.length, - itemBuilder: (ctx, index) => moveEntry(moves[index])); - } - - @override - Widget build(BuildContext context) { - return Container( - child: list(), - ); - } -} diff --git a/packages/mobility_features/mobility_features_example/lib/places_page.dart b/packages/mobility_features/mobility_features_example/lib/places_page.dart deleted file mode 100644 index 1232f75af..000000000 --- a/packages/mobility_features/mobility_features_example/lib/places_page.dart +++ /dev/null @@ -1,34 +0,0 @@ -part of mobility_app; - -class PlacesPage extends StatelessWidget { - final List places; - - PlacesPage(this.places); - - Widget placeEntry(Place p) { - String lat = p.geoLocation.latitude.toStringAsFixed(4); - String lon = p.geoLocation.longitude.toStringAsFixed(4); - - return Container( - padding: const EdgeInsets.all(2), - margin: EdgeInsets.all(3), - child: ListTile( - leading: Text('Place ID ${p.id}'), - title: Text('$lat, $lon'), - trailing: Text('${formatDuration(p.duration)}'), - )); - } - - Widget list() { - return ListView.builder( - itemCount: places.length, - itemBuilder: (ctx, index) => placeEntry(places[index])); - } - - @override - Widget build(BuildContext context) { - return Container( - child: list(), - ); - } -} diff --git a/packages/mobility_features/mobility_features_example/lib/stops_page.dart b/packages/mobility_features/mobility_features_example/lib/stops_page.dart deleted file mode 100644 index 66e6bb4fd..000000000 --- a/packages/mobility_features/mobility_features_example/lib/stops_page.dart +++ /dev/null @@ -1,33 +0,0 @@ -part of mobility_app; - -class StopsPage extends StatelessWidget { - final List stops; - - StopsPage(this.stops); - - Widget stopEntry(Stop s) { - String lat = s.geoLocation.latitude.toStringAsFixed(4); - String lon = s.geoLocation.longitude.toStringAsFixed(4); - return Container( - padding: const EdgeInsets.all(2), - margin: EdgeInsets.all(3), - child: ListTile( - leading: Text('Place ${s.placeId}'), - title: Text('${interval(s.arrival, s.departure)}'), - trailing: Text('$lat, $lon'), - )); - } - - Widget list() { - return ListView.builder( - itemCount: stops.length, - itemBuilder: (ctx, index) => stopEntry(stops[index])); - } - - @override - Widget build(BuildContext context) { - return Container( - child: list(), - ); - } -} diff --git a/packages/mobility_features/mobility_features_example/pubspec.yaml b/packages/mobility_features/mobility_features_example/pubspec.yaml deleted file mode 100644 index 832b4121f..000000000 --- a/packages/mobility_features/mobility_features_example/pubspec.yaml +++ /dev/null @@ -1,80 +0,0 @@ -name: mobility_features_example -description: A new Flutter project. - -# The following line prevents the package from being accidentally published to -# pub.dev using `pub publish`. This is preferred for private packages. -publish_to: 'none' # Remove this line if you wish to publish to pub.dev - -# The following defines the version and build number for your application. -# A version number is three numbers separated by dots, like 1.2.43 -# followed by an optional build number separated by a +. -# Both the version and the builder number may be overridden in flutter -# build by specifying --build-name and --build-number, respectively. -# In Android, build-name is used as versionName while build-number used as versionCode. -# Read more about Android versioning at https://developer.android.com/studio/publish/versioning -# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. -# Read more about iOS versioning at -# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html -version: 1.0.0+1 - -environment: - sdk: ">=2.7.0 <3.0.0" - -dependencies: - flutter: - sdk: flutter - - geolocator: - mobility_features: - path: ../ - carp_background_location: ^1.0.3 - - # The following adds the Cupertino Icons font to your application. - # Use with the CupertinoIcons class for iOS style icons. - cupertino_icons: ^0.1.3 - -dev_dependencies: - flutter_test: - sdk: flutter - -# For information on the generic Dart part of this file, see the -# following page: https://dart.dev/tools/pub/pubspec - -# The following section is specific to Flutter. -flutter: - - # The following line ensures that the Material Icons font is - # included with your application, so that you can use the icons in - # the material Icons class. - uses-material-design: true - - # To add assets to your application, add an assets section, like this: - # assets: - # - images/a_dot_burr.jpeg - # - images/a_dot_ham.jpeg - - # An image asset can refer to one or more resolution-specific "variants", see - # https://flutter.dev/assets-and-images/#resolution-aware. - - # For details regarding adding assets from package dependencies, see - # https://flutter.dev/assets-and-images/#from-packages - - # To add custom fonts to your application, add a fonts section here, - # in this "flutter" section. Each entry in this list should have a - # "family" key with the font family name, and a "fonts" key with a - # list giving the asset and other descriptors for the font. For - # example: - # fonts: - # - family: Schyler - # fonts: - # - asset: fonts/Schyler-Regular.ttf - # - asset: fonts/Schyler-Italic.ttf - # style: italic - # - family: Trajan Pro - # fonts: - # - asset: fonts/TrajanPro.ttf - # - asset: fonts/TrajanPro_Bold.ttf - # weight: 700 - # - # For details regarding fonts from package dependencies, - # see https://flutter.dev/custom-fonts/#from-packages diff --git a/packages/mobility_features/pubspec.yaml b/packages/mobility_features/pubspec.yaml index 880fb08b3..030c43ccb 100644 --- a/packages/mobility_features/pubspec.yaml +++ b/packages/mobility_features/pubspec.yaml @@ -1,6 +1,6 @@ name: mobility_features description: Calculation of real-time mobility features like places, stops, and home stay -version: 3.0.0+2 +version: 3.1.0 homepage: https://github.com/cph-cachet/flutter-plugins/ environment: diff --git a/packages/mobility_features/test/mobility_features_test.dart b/packages/mobility_features/test/mobility_features_test.dart index ce84600da..df215cbf3 100644 --- a/packages/mobility_features/test/mobility_features_test.dart +++ b/packages/mobility_features/test/mobility_features_test.dart @@ -413,9 +413,9 @@ void main() async { print("STOPS"); printList(event.stops); print("ALL PLACES"); - printList(event.places!); + printList(event.places); print("SIGNIFICANT PLACES"); - printList(event.significantPlaces!); + printList(event.significantPlaces); print( "TOTAL DURATION (STOPS): ${event.stops.map((p) => p.duration).reduce((a, b) => a + b)}"); }, count: 283)); diff --git a/packages/mobility_features/test/testdata/location_samples.json b/packages/mobility_features/test/testdata/location_samples.json index 9ba3251e6..3f290383b 100644 --- a/packages/mobility_features/test/testdata/location_samples.json +++ b/packages/mobility_features/test/testdata/location_samples.json @@ -68,3 +68,23 @@ {"geo_location":{"latitude":48.17132510439827,"longitude":11.563184492969626},"datetime":"1581633592554"} {"geo_location":{"latitude":48.171309745958155,"longitude":11.563212691472879},"datetime":"1581633598543"} {"geo_location":{"latitude":48.17130980868023,"longitude":11.563223221990256},"datetime":"1581633604549"} +{"geo_location":{"latitude":48.17130116552479,"longitude":11.563204770731598},"datetime":"1581633610547"} +{"geo_location":{"latitude":48.17132024048344,"longitude":11.563197098288784},"datetime":"1581633616553"} +{"geo_location":{"latitude":48.171299319142854,"longitude":11.563184923043417},"datetime":"1581633622547"} +{"geo_location":{"latitude":48.171310790416754,"longitude":11.563198310967506},"datetime":"1581633628544"} +{"geo_location":{"latitude":48.17131589082105,"longitude":11.563213775430425},"datetime":"1581633634615"} +{"geo_location":{"latitude":48.17131124461477,"longitude":11.5632183996362},"datetime":"1581633640550"} +{"geo_location":{"latitude":48.17132126286372,"longitude":11.563221820856478},"datetime":"1581633646551"} +{"geo_location":{"latitude":48.17130773221799,"longitude":11.56319857289543},"datetime":"1581633652555"} +{"geo_location":{"latitude":48.17133161288919,"longitude":11.563206664336509},"datetime":"1581633658545"} +{"geo_location":{"latitude":48.17132725995403,"longitude":11.563204133995162},"datetime":"1581633664545"} +{"geo_location":{"latitude":48.17131174010998,"longitude":11.563214543661774},"datetime":"1581633670547"} +{"geo_location":{"latitude":48.17131006002293,"longitude":11.563231760120583},"datetime":"1581633676549"} +{"geo_location":{"latitude":48.17131857591096,"longitude":11.563203957262328},"datetime":"1581633682549"} +{"geo_location":{"latitude":48.1713001793906,"longitude":11.563207264402903},"datetime":"1581633688547"} +{"geo_location":{"latitude":48.171320995827394,"longitude":11.56320571687284},"datetime":"1581633694551"} +{"geo_location":{"latitude":48.171289834325194,"longitude":11.563235278192037},"datetime":"1581633700537"} +{"geo_location":{"latitude":48.171305118409826,"longitude":11.563221094539754},"datetime":"1581633706547"} +{"geo_location":{"latitude":48.171306756445375,"longitude":11.563214587831721},"datetime":"1581633712549"} +{"geo_location":{"latitude":48.17129139009002,"longitude":11.563199191004612},"datetime":"1581633718547"} +{"geo_location":{"latitude":48.17131649786042,"longitude":11.56319249730382},"datetime":"1581633724548"} From 552dc1b9b417533b66aba8e6e73dd7c4d480fcfe Mon Sep 17 00:00:00 2001 From: bardram Date: Sun, 9 Jan 2022 21:29:17 +0100 Subject: [PATCH 22/30] health version: 3.4.1 published --- .gitignore | 1 + packages/health/CHANGELOG.md | 14 +++++++++----- .../health/example/.flutter-plugins-dependencies | 2 +- packages/health/pubspec.yaml | 2 +- 4 files changed, 12 insertions(+), 7 deletions(-) diff --git a/.gitignore b/.gitignore index ed6117258..be3b22e67 100644 --- a/.gitignore +++ b/.gitignore @@ -127,3 +127,4 @@ packages/health/example/.flutter-plugins-dependencies packages/activity_recognition_flutter/example/.flutter-plugins-dependencies packages/mobility_features/example/.flutter-plugins-dependencies packages/mobility_features/example/.flutter-plugins-dependencies +packages/health/example/.flutter-plugins-dependencies diff --git a/packages/health/CHANGELOG.md b/packages/health/CHANGELOG.md index 5451eba4e..588525be8 100644 --- a/packages/health/CHANGELOG.md +++ b/packages/health/CHANGELOG.md @@ -1,9 +1,13 @@ +## 3.4.1 +* Resolved concurrent issues with native threads [PR #483](https://github.com/cph-cachet/flutter-plugins/pull/483). +* Healthkit CategorySample [PR #485](https://github.com/cph-cachet/flutter-plugins/pull/485). + ## 3.4.0 -* [PR #457](https://github.com/cph-cachet/flutter-plugins/pull/457) - Add sleep in bed to android -* [PR #458](https://github.com/cph-cachet/flutter-plugins/pull/458) - Add the android.permission.ACTIVITY_RECOGNITION setup to the README -* [PR #462](https://github.com/cph-cachet/flutter-plugins/pull/462) - Fixed (regression) issues with metric and permissions -* [PR #471](https://github.com/cph-cachet/flutter-plugins/pull/471) - Get total steps -* update of example app to refelct new features. +* Add sleep in bed to android [PR #457](https://github.com/cph-cachet/flutter-plugins/pull/457). +* Add the android.permission.ACTIVITY_RECOGNITION setup to the README [PR #458](https://github.com/cph-cachet/flutter-plugins/pull/458). +* Fixed (regression) issues with metric and permissions [PR #462](https://github.com/cph-cachet/flutter-plugins/pull/462). +* Get total steps [PR #471](https://github.com/cph-cachet/flutter-plugins/pull/471). +* update of example app to refelct new features. * update of API documentation. ## 3.3.1 diff --git a/packages/health/example/.flutter-plugins-dependencies b/packages/health/example/.flutter-plugins-dependencies index 4768c46ee..7e1540f6d 100644 --- a/packages/health/example/.flutter-plugins-dependencies +++ b/packages/health/example/.flutter-plugins-dependencies @@ -1 +1 @@ -{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"device_info_plus","path":"/Users/bardram/.pub-cache/hosted/pub.dartlang.org/device_info_plus-3.1.1/","dependencies":[]},{"name":"health","path":"/Users/bardram/dev/flutter-plugins/packages/health/","dependencies":["device_info_plus"]}],"android":[{"name":"device_info_plus","path":"/Users/bardram/.pub-cache/hosted/pub.dartlang.org/device_info_plus-3.1.1/","dependencies":[]},{"name":"health","path":"/Users/bardram/dev/flutter-plugins/packages/health/","dependencies":["device_info_plus"]}],"macos":[{"name":"device_info_plus_macos","path":"/Users/bardram/.pub-cache/hosted/pub.dartlang.org/device_info_plus_macos-2.2.0/","dependencies":[]}],"linux":[],"windows":[],"web":[{"name":"device_info_plus_web","path":"/Users/bardram/.pub-cache/hosted/pub.dartlang.org/device_info_plus_web-2.1.0/","dependencies":[]}]},"dependencyGraph":[{"name":"device_info_plus","dependencies":["device_info_plus_macos","device_info_plus_web"]},{"name":"device_info_plus_macos","dependencies":[]},{"name":"device_info_plus_web","dependencies":[]},{"name":"health","dependencies":["device_info_plus"]}],"date_created":"2021-12-29 21:18:27.488582","version":"2.5.3"} \ No newline at end of file +{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"device_info_plus","path":"/Users/bardram/.pub-cache/hosted/pub.dartlang.org/device_info_plus-3.1.1/","dependencies":[]},{"name":"health","path":"/Users/bardram/dev/flutter-plugins/packages/health/","dependencies":["device_info_plus"]}],"android":[{"name":"device_info_plus","path":"/Users/bardram/.pub-cache/hosted/pub.dartlang.org/device_info_plus-3.1.1/","dependencies":[]},{"name":"health","path":"/Users/bardram/dev/flutter-plugins/packages/health/","dependencies":["device_info_plus"]}],"macos":[{"name":"device_info_plus_macos","path":"/Users/bardram/.pub-cache/hosted/pub.dartlang.org/device_info_plus_macos-2.2.0/","dependencies":[]}],"linux":[],"windows":[],"web":[{"name":"device_info_plus_web","path":"/Users/bardram/.pub-cache/hosted/pub.dartlang.org/device_info_plus_web-2.1.0/","dependencies":[]}]},"dependencyGraph":[{"name":"device_info_plus","dependencies":["device_info_plus_macos","device_info_plus_web"]},{"name":"device_info_plus_macos","dependencies":[]},{"name":"device_info_plus_web","dependencies":[]},{"name":"health","dependencies":["device_info_plus"]}],"date_created":"2022-01-09 21:27:45.012627","version":"2.5.3"} \ No newline at end of file diff --git a/packages/health/pubspec.yaml b/packages/health/pubspec.yaml index 0b85d3df6..d2e2c8aa4 100644 --- a/packages/health/pubspec.yaml +++ b/packages/health/pubspec.yaml @@ -1,6 +1,6 @@ name: health description: Wrapper for the iOS HealthKit and Android GoogleFit services. -version: 3.4.0 +version: 3.4.1 homepage: https://github.com/cph-cachet/flutter-plugins/tree/master/packages/health environment: From f0efe2308c0310f61da5455484a2b6d7fc6b55c4 Mon Sep 17 00:00:00 2001 From: bardram Date: Mon, 10 Jan 2022 09:23:56 +0100 Subject: [PATCH 23/30] update API and README docs in health plugin --- packages/health/CHANGELOG.md | 31 +++++++++++---------- packages/health/README.md | 12 +++++++- packages/health/lib/src/health_factory.dart | 15 ++++++++-- 3 files changed, 40 insertions(+), 18 deletions(-) diff --git a/packages/health/CHANGELOG.md b/packages/health/CHANGELOG.md index 588525be8..7dd14d0c1 100644 --- a/packages/health/CHANGELOG.md +++ b/packages/health/CHANGELOG.md @@ -1,38 +1,39 @@ -## 3.4.1 -* Resolved concurrent issues with native threads [PR #483](https://github.com/cph-cachet/flutter-plugins/pull/483). -* Healthkit CategorySample [PR #485](https://github.com/cph-cachet/flutter-plugins/pull/485). +## 3.4.2 +* Resolved concurrent issues with native threads [PR#483](https://github.com/cph-cachet/flutter-plugins/pull/483). +* Healthkit CategorySample [PR#485](https://github.com/cph-cachet/flutter-plugins/pull/485). +* update of API documentation. ## 3.4.0 -* Add sleep in bed to android [PR #457](https://github.com/cph-cachet/flutter-plugins/pull/457). -* Add the android.permission.ACTIVITY_RECOGNITION setup to the README [PR #458](https://github.com/cph-cachet/flutter-plugins/pull/458). -* Fixed (regression) issues with metric and permissions [PR #462](https://github.com/cph-cachet/flutter-plugins/pull/462). -* Get total steps [PR #471](https://github.com/cph-cachet/flutter-plugins/pull/471). +* Add sleep in bed to android [PR#457](https://github.com/cph-cachet/flutter-plugins/pull/457). +* Add the android.permission.ACTIVITY_RECOGNITION setup to the README [PR#458](https://github.com/cph-cachet/flutter-plugins/pull/458). +* Fixed (regression) issues with metric and permissions [PR#462](https://github.com/cph-cachet/flutter-plugins/pull/462). +* Get total steps [PR#471](https://github.com/cph-cachet/flutter-plugins/pull/471). * update of example app to refelct new features. * update of API documentation. ## 3.3.1 -* [PR #428](https://github.com/cph-cachet/flutter-plugins/pull/428) - DISTANCE_DELTA is for Android, not iOS -* [PR #454](https://github.com/cph-cachet/flutter-plugins/pull/454) - added missing READ_ACCESS +* DISTANCE_DELTA is for Android, not iOS [PR#428](https://github.com/cph-cachet/flutter-plugins/pull/428). +* added missing READ_ACCESS [PR#454](https://github.com/cph-cachet/flutter-plugins/pull/454). ## 3.3.0 -* [PR #430](https://github.com/cph-cachet/flutter-plugins/pull/430) - Write support on Google Fit and HealthKit +* Write support on Google Fit and HealthKit [PR#430](https://github.com/cph-cachet/flutter-plugins/pull/430). ## 3.2.1 * Updated `device_info_plus` version dependency ## 3.2.0 -* [PR #421](https://github.com/cph-cachet/flutter-plugins/pull/421) - added simple `HKWorkout` and `ExerciseTime` support +* added simple `HKWorkout` and `ExerciseTime` support [PR#421](https://github.com/cph-cachet/flutter-plugins/pull/421). ## 3.1.1+1 -* [PR #394](https://github.com/cph-cachet/flutter-plugins/pull/394) - added functions to request authorization +* added functions to request authorization [PR#394](https://github.com/cph-cachet/flutter-plugins/pull/394) ## 3.1.0 -* [PR #372](https://github.com/cph-cachet/flutter-plugins/pull/372) - added sleep data to Android + fix of permissions and initialization -* [PR #388](https://github.com/cph-cachet/flutter-plugins/pull/388) - testability of HealthDataPoint +* added sleep data to Android + fix of permissions and initialization [PR#372](https://github.com/cph-cachet/flutter-plugins/pull/372) +* testability of HealthDataPoint [PR#388](https://github.com/cph-cachet/flutter-plugins/pull/388). * update to using the `device_info_plus` plugin ## 3.0.6 -* [PR#281](https://github.com/cph-cachet/flutter-plugins/pull/281) Added two new fields to the `HealthDataPoint` - `SourceId` and `SourceName` and populate when data is read. This allows datapoints to be disambigous and in some cases allows us to get more accurate data. For example the number of steps can be reported from Apple Health and Watch and without source data they are aggregated into just "steps" producing an innacurate result. +* Added two new fields to the `HealthDataPoint` - `SourceId` and `SourceName` and populate when data is read. This allows datapoints to be disambigous and in some cases allows us to get more accurate data. For example the number of steps can be reported from Apple Health and Watch and without source data they are aggregated into just "steps" producing an innacurate result [PR#281](https://github.com/cph-cachet/flutter-plugins/pull/281). ## 3.0.5 * Null safety in Dart has been implemented diff --git a/packages/health/README.md b/packages/health/README.md index bbec641ce..b35e3b82e 100644 --- a/packages/health/README.md +++ b/packages/health/README.md @@ -1,6 +1,16 @@ # Health -Enables reading and writing health data from/to Apple Health and Google Fit. Also allow for getting the total number of steps for a specific time period. +Enables reading and writing health data from/to Apple Health and Google Fit. + +The plugin supports: + + * handling permissions to access health data using the `hasPermissions`, + `requestPermissions`, `requestAuthorization`, `revokePermissions` methods. + * reading health data using the `getHealthDataFromTypes` method. + * writing health data using the `writeHealthData` method. + * accessing total step counts using the `getTotalStepsInInterval` method. + * cleaning up dublicate data points via the `removeDuplicates` method. + > For Android, the target phone __needs__ to have [Google Fit](https://www.google.com/fit/) installed and have access to the internet, otherwise this plugin will not work. diff --git a/packages/health/lib/src/health_factory.dart b/packages/health/lib/src/health_factory.dart index acd1d7336..3b9aa4f67 100644 --- a/packages/health/lib/src/health_factory.dart +++ b/packages/health/lib/src/health_factory.dart @@ -1,6 +1,15 @@ part of health; /// Main class for the Plugin. +/// +/// The plugin supports: +/// +/// * handling permissions to access health data using the [hasPermissions], +/// [requestPermissions], [requestAuthorization], [revokePermissions] methods. +/// * reading health data using the [getHealthDataFromTypes] method. +/// * writing health data using the [writeHealthData] method. +/// * accessing total step counts using the [getTotalStepsInInterval] method. +/// * cleaning up dublicate data points via the [removeDuplicates] method. class HealthFactory { static const MethodChannel _channel = MethodChannel('flutter_health'); String? _deviceId; @@ -88,8 +97,10 @@ class HealthFactory { /// + If unspecified, each [HealthDataType] in [types] is requested for READ [HealthDataAccess]. /// + If specified, each [HealthDataAccess] in this list is requested for its corresponding indexed /// entry in [types]. In addition, the length of this list must be equal to that of [types]. - Future requestAuthorization(List types, - {List? permissions}) async { + Future requestAuthorization( + List types, { + List? permissions, + }) async { if (permissions != null && permissions.length != types.length) { throw ArgumentError( 'The length of [types] must be same as that of [permissions].'); From d5f229e08e9c3cc6dab3c5b061788040d8406cea Mon Sep 17 00:00:00 2001 From: bardram Date: Mon, 10 Jan 2022 09:34:49 +0100 Subject: [PATCH 24/30] misc cleanup and refactor in health plugin --- packages/health/lib/health.dart | 1 - packages/health/lib/src/data_types.dart | 24 ++++++++++++-- packages/health/lib/src/functions.dart | 19 ++++++------ .../health/lib/src/health_data_point.dart | 31 +++++++------------ packages/health/lib/src/health_factory.dart | 2 +- packages/health/lib/src/units.dart | 21 ------------- 6 files changed, 44 insertions(+), 54 deletions(-) delete mode 100644 packages/health/lib/src/units.dart diff --git a/packages/health/lib/health.dart b/packages/health/lib/health.dart index 4fa0a9014..76679e077 100644 --- a/packages/health/lib/health.dart +++ b/packages/health/lib/health.dart @@ -12,4 +12,3 @@ part 'src/data_types.dart'; part 'src/functions.dart'; part 'src/health_data_point.dart'; part 'src/health_factory.dart'; -part 'src/units.dart'; diff --git a/packages/health/lib/src/data_types.dart b/packages/health/lib/src/data_types.dart index 4eb484a44..ea340003d 100644 --- a/packages/health/lib/src/data_types.dart +++ b/packages/health/lib/src/data_types.dart @@ -110,7 +110,7 @@ const List _dataTypeKeysAndroid = [ HealthDataType.WATER, ]; -/// Map a [HealthDataType] to a [HealthDataUnit]. +/// Maps a [HealthDataType] to a [HealthDataUnit]. const Map _dataTypeToUnit = { HealthDataType.ACTIVE_ENERGY_BURNED: HealthDataUnit.CALORIES, HealthDataType.BASAL_ENERGY_BURNED: HealthDataUnit.CALORIES, @@ -147,7 +147,7 @@ const Map _dataTypeToUnit = { HealthDataType.EXERCISE_TIME: HealthDataUnit.MINUTES, HealthDataType.WORKOUT: HealthDataUnit.MINUTES, - /// Heart Rate events (specific to Apple Watch) + // Heart Rate events (specific to Apple Watch) HealthDataType.HIGH_HEART_RATE_EVENT: HealthDataUnit.NO_UNIT, HealthDataType.LOW_HEART_RATE_EVENT: HealthDataUnit.NO_UNIT, HealthDataType.IRREGULAR_HEART_RATE_EVENT: HealthDataUnit.NO_UNIT, @@ -212,3 +212,23 @@ const PlatformTypeJsonValue = { PlatformType.IOS: 'ios', PlatformType.ANDROID: 'android' }; + +/// List of all [HealthDataPoint] units. +enum HealthDataUnit { + BEATS_PER_MINUTE, + CALORIES, + COUNT, + DEGREE_CELSIUS, + GRAMS, + KILOGRAMS, + METERS, + MILLIGRAM_PER_DECILITER, + MILLIMETER_OF_MERCURY, + MILLISECONDS, + MINUTES, + NO_UNIT, + PERCENTAGE, + SIEMENS, + UNKNOWN_UNIT, + LITER, +} diff --git a/packages/health/lib/src/functions.dart b/packages/health/lib/src/functions.dart index 56f0b7ba1..e58cb306e 100644 --- a/packages/health/lib/src/functions.dart +++ b/packages/health/lib/src/functions.dart @@ -1,20 +1,19 @@ part of health; -/// Custom Exception for the plugin, -/// thrown whenever a Health Data Type is requested, -/// when not available on the current platform -class _HealthException implements Exception { - dynamic _dataType; - String _cause; +/// Custom Exception for the plugin. Used when a Health Data Type is requested, +/// but not available on the current platform. +class HealthException implements Exception { + dynamic dataType; + String cause; - _HealthException(this._dataType, this._cause); + HealthException(this.dataType, this.cause); - String toString() { - return "An exception happened when requesting ${_dataType.toString()}. Cause: $_cause"; - } + String toString() => + "Error requesting health data type '$dataType' - cause: $cause"; } /// Extracts the string value from an enum String _enumToString(enumItem) => enumItem.toString().split('.').last; +/// A list of supported platforms. enum PlatformType { IOS, ANDROID } diff --git a/packages/health/lib/src/health_data_point.dart b/packages/health/lib/src/health_data_point.dart index fea3621db..c9354551a 100644 --- a/packages/health/lib/src/health_data_point.dart +++ b/packages/health/lib/src/health_data_point.dart @@ -1,7 +1,7 @@ part of health; /// A [HealthDataPoint] object corresponds to a data point captures from -/// GoogleFit or Apple HealthKit +/// GoogleFit or Apple HealthKit. class HealthDataPoint { num _value; HealthDataType _type; @@ -77,45 +77,39 @@ class HealthDataPoint { 'sourceId: $sourceId,' 'sourceName: $sourceName,'; - /// Get the quantity value of the data point + /// The quantity value of the data point num get value => _value; - /// Get the start of the datetime interval + /// The start of the time interval DateTime get dateFrom => _dateFrom; - /// Get the end of the datetime interval + /// The end of the time interval DateTime get dateTo => _dateTo; - /// Get the type of the data point + /// The type of the data point HealthDataType get type => _type; - /// Get the unit of the data point + /// The unit of the data point HealthDataUnit get unit => _unit; - /// Get the software platform of the data point - /// (i.e. Android or iOS) + /// The software platform of the data point PlatformType get platform => _platform; - /// Get the data point type as a string + /// The data point type as a string String get typeString => _enumToString(_type); - /// Get the data point unit as a string + /// The data point unit as a string String get unitString => _enumToString(_unit); - /// Get the id of the device from which - /// the data point was extracted + /// The id of the device from which the data point was fetched. String get deviceId => _deviceId; - /// Get the id of the source from which - /// the data point was extracted + /// The id of the source from which the data point was fetched. String get sourceId => _sourceId; - /// Get the name of the source from which - /// the data point was extracted + /// The name of the source from which the data point was fetched. String get sourceName => _sourceName; - /// An equals (==) operator for comparing two data points - /// This makes it possible to remove duplicate data points. @override bool operator ==(Object o) { return o is HealthDataPoint && @@ -130,7 +124,6 @@ class HealthDataPoint { this.sourceName == o.sourceName; } - /// Override required due to overriding the '==' operator @override int get hashCode => toJson().hashCode; } diff --git a/packages/health/lib/src/health_factory.dart b/packages/health/lib/src/health_factory.dart index 3b9aa4f67..30b7dd04d 100644 --- a/packages/health/lib/src/health_factory.dart +++ b/packages/health/lib/src/health_factory.dart @@ -229,7 +229,7 @@ class HealthFactory { // If not implemented on platform, throw an exception if (!isDataTypeAvailable(dataType)) { - throw _HealthException( + throw HealthException( dataType, 'Not available on platform $_platformType'); } diff --git a/packages/health/lib/src/units.dart b/packages/health/lib/src/units.dart deleted file mode 100644 index c24a56fe8..000000000 --- a/packages/health/lib/src/units.dart +++ /dev/null @@ -1,21 +0,0 @@ -part of health; - -/// List of all [HealthDataPoint] units. -enum HealthDataUnit { - BEATS_PER_MINUTE, - CALORIES, - COUNT, - DEGREE_CELSIUS, - GRAMS, - KILOGRAMS, - METERS, - MILLIGRAM_PER_DECILITER, - MILLIMETER_OF_MERCURY, - MILLISECONDS, - MINUTES, - NO_UNIT, - PERCENTAGE, - SIEMENS, - UNKNOWN_UNIT, - LITER, -} From cb2b83011dcca39680c232afebde7b14c15a20e7 Mon Sep 17 00:00:00 2001 From: bardram Date: Mon, 10 Jan 2022 09:36:00 +0100 Subject: [PATCH 25/30] Update pubspec.yaml --- packages/health/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/health/pubspec.yaml b/packages/health/pubspec.yaml index d2e2c8aa4..057500fb3 100644 --- a/packages/health/pubspec.yaml +++ b/packages/health/pubspec.yaml @@ -1,6 +1,6 @@ name: health description: Wrapper for the iOS HealthKit and Android GoogleFit services. -version: 3.4.1 +version: 3.4.2 homepage: https://github.com/cph-cachet/flutter-plugins/tree/master/packages/health environment: From bb94add2abfc450dca65b7080cc93c90383a8fe8 Mon Sep 17 00:00:00 2001 From: bardram Date: Mon, 10 Jan 2022 09:37:53 +0100 Subject: [PATCH 26/30] Update README.md --- packages/health/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/health/README.md b/packages/health/README.md index b35e3b82e..e42508779 100644 --- a/packages/health/README.md +++ b/packages/health/README.md @@ -12,7 +12,7 @@ The plugin supports: * cleaning up dublicate data points via the `removeDuplicates` method. -> For Android, the target phone __needs__ to have [Google Fit](https://www.google.com/fit/) installed and have access to the internet, otherwise this plugin will not work. +Note that for Android, the target phone __needs__ to have [Google Fit](https://www.google.com/fit/) installed and have access to the internet, otherwise this plugin will not work. ## Data Types From 353c93ebf58aecae40a215f1f0bb8af610c4e195 Mon Sep 17 00:00:00 2001 From: bardram Date: Mon, 10 Jan 2022 09:50:14 +0100 Subject: [PATCH 27/30] fix #401 --- packages/health/lib/src/health_data_point.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/health/lib/src/health_data_point.dart b/packages/health/lib/src/health_data_point.dart index c9354551a..4ec871153 100644 --- a/packages/health/lib/src/health_data_point.dart +++ b/packages/health/lib/src/health_data_point.dart @@ -66,14 +66,14 @@ class HealthDataPoint { 'source_name': sourceName }; - /// Converts the [HealthDataPoint] to a string + @override String toString() => '${this.runtimeType} - ' 'value: $value, ' 'unit: $unit, ' 'dateFrom: $dateFrom, ' 'dateTo: $dateTo, ' 'dataType: $type,' - 'platform: $platform' + 'platform: $platform,' 'sourceId: $sourceId,' 'sourceName: $sourceName,'; From 21695c0f6d207ef634f6d11a0b6944d4ca9d0b59 Mon Sep 17 00:00:00 2001 From: bardram Date: Mon, 10 Jan 2022 09:54:46 +0100 Subject: [PATCH 28/30] health 3.4.3 published --- packages/health/CHANGELOG.md | 3 +++ packages/health/lib/src/health_data_point.dart | 8 ++++---- packages/health/pubspec.yaml | 2 +- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/packages/health/CHANGELOG.md b/packages/health/CHANGELOG.md index 7dd14d0c1..eb5af0bbe 100644 --- a/packages/health/CHANGELOG.md +++ b/packages/health/CHANGELOG.md @@ -1,3 +1,6 @@ +## 3.4.3 +* fix of [#401](https://github.com/cph-cachet/flutter-plugins/issues/401). + ## 3.4.2 * Resolved concurrent issues with native threads [PR#483](https://github.com/cph-cachet/flutter-plugins/pull/483). * Healthkit CategorySample [PR#485](https://github.com/cph-cachet/flutter-plugins/pull/485). diff --git a/packages/health/lib/src/health_data_point.dart b/packages/health/lib/src/health_data_point.dart index 4ec871153..d4bef4e4e 100644 --- a/packages/health/lib/src/health_data_point.dart +++ b/packages/health/lib/src/health_data_point.dart @@ -72,10 +72,10 @@ class HealthDataPoint { 'unit: $unit, ' 'dateFrom: $dateFrom, ' 'dateTo: $dateTo, ' - 'dataType: $type,' - 'platform: $platform,' - 'sourceId: $sourceId,' - 'sourceName: $sourceName,'; + 'dataType: $type, ' + 'platform: $platform, ' + 'sourceId: $sourceId, ' + 'sourceName: $sourceName'; /// The quantity value of the data point num get value => _value; diff --git a/packages/health/pubspec.yaml b/packages/health/pubspec.yaml index 057500fb3..4ee7fc766 100644 --- a/packages/health/pubspec.yaml +++ b/packages/health/pubspec.yaml @@ -1,6 +1,6 @@ name: health description: Wrapper for the iOS HealthKit and Android GoogleFit services. -version: 3.4.2 +version: 3.4.3 homepage: https://github.com/cph-cachet/flutter-plugins/tree/master/packages/health environment: From ab460e41e72438b5d62db07a938d3bb15b3407ca Mon Sep 17 00:00:00 2001 From: "Jakob E. Bardram" Date: Wed, 12 Jan 2022 15:29:48 +0100 Subject: [PATCH 29/30] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b54df5bf2..587ed6bd9 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ For more information about plugins, and how to use them, see ## Plugins These are the available plugins in this repository. -| Plugin | Description | Android | iOS | Pub | +| Plugin | Description | Android | iOS | http://pub.dev/ | |--------|-------------|:-------:|:---:|:---------:| | [screen_state](./packages/screen_state) | Track screen state changes | ✔️ | ❌ | [![pub package](https://img.shields.io/pub/v/screen_state.svg)](https://pub.dartlang.org/packages/screen_state) | | [light](./packages/light) | Track light sensor readings | ✔️ | ❌ | [![pub package](https://img.shields.io/pub/v/light.svg)](https://pub.dartlang.org/packages/light) | From c15b6c12b2393a25121099447c05cb01168d9d55 Mon Sep 17 00:00:00 2001 From: "Jakob E. Bardram" Date: Wed, 12 Jan 2022 15:35:50 +0100 Subject: [PATCH 30/30] Update README.md --- README.md | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 587ed6bd9..ddedf068b 100644 --- a/README.md +++ b/README.md @@ -35,7 +35,15 @@ Please check existing issues and file any new issues, bugs, or feature requests ## Contributing -If you wish to contribute a new plugin to the Flutter ecosystem, please +As part of the open-source Flutter ecosystem, we would welcome any help in maintaining and enhancing these plugins. +We (i.e., CACHET) have limited resources for maintaining these plugins and we rely on **your** help in this. +We welcome any contribution -- from small error corrections in the documentation, to bug fixes, to large features enhacements, or even new features in a plugin. +If you wish to contribute to any of the plugins in this repo, +please review our [contribution guide](https://github.com/cph-cachet/flutter-plugins/CONTRIBUTING.md), +and send a [pull request](https://github.com/cph-cachet/flutter-plugins/pulls). + + +In general, if you wish to contribute a new plugin to the Flutter ecosystem, please see the documentation for [developing packages](https://flutter.io/developing-packages/) and [platform channels](https://flutter.io/platform-channels/). You can store your plugin source code in any GitHub repository (the present repo is only @@ -43,7 +51,3 @@ intended for plugins developed by the core CARP team). Once your plugin is ready you can [publish](https://flutter.io/developing-packages/#publish) to the [pub repository](https://pub.dartlang.org/). -If you wish to contribute a change to any of the existing plugins in this repo, -please review our [contribution guide](https://github.com/cph-cachet/flutter-plugins/CONTRIBUTING.md), -and send a [pull request](https://github.com/cph-cachet/flutter-plugins/pulls). -