@tryterra/react-native 0.0.0-stage → 2.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +142 -2
- package/TerraReactNative.podspec +48 -0
- package/android/build.gradle +87 -0
- package/android/src/bridgeCore/kotlin/README.md +1 -0
- package/android/src/bridgeCore/kotlin/co/tryterra/sdk/bridge/BridgeBootstrap.kt +133 -0
- package/android/src/bridgeCore/kotlin/co/tryterra/sdk/bridge/BridgeHost.kt +32 -0
- package/android/src/bridgeCore/kotlin/co/tryterra/sdk/bridge/Conversions.kt +557 -0
- package/android/src/bridgeCore/kotlin/co/tryterra/sdk/bridge/LauncherSlots.kt +41 -0
- package/android/src/bridgeCore/kotlin/co/tryterra/sdk/bridge/ReadConversions.kt +250 -0
- package/android/src/bridgeCore/kotlin/co/tryterra/sdk/bridge/Rejections.kt +150 -0
- package/android/src/bridgeCore/kotlin/co/tryterra/sdk/bridge/StreamRegistry.kt +77 -0
- package/android/src/bridgeCore/kotlin/co/tryterra/sdk/bridge/TerraBridge.kt +86 -0
- package/android/src/bridgeCore/kotlin/co/tryterra/sdk/bridge/TerraBridgeOpsImpl.kt +300 -0
- package/android/src/bridgeCore/kotlin/co/tryterra/sdk/bridge/TerraBridgeStreamsImpl.kt +50 -0
- package/android/src/bridgeCore/kotlin/co/tryterra/sdk/bridge/TokenBroker.kt +198 -0
- package/android/src/bridgeCore/kotlin/co/tryterra/sdk/bridge/generated/BridgeErrors.kt +217 -0
- package/android/src/bridgeCore/kotlin/co/tryterra/sdk/bridge/generated/BridgeOps.kt +582 -0
- package/android/src/bridgeCore/kotlin/co/tryterra/sdk/bridge/generated/BridgeTypes.kt +1569 -0
- package/android/src/healthConnectOnly/kotlin/co/tryterra/sdk/reactnative/TerraStores.kt +11 -0
- package/android/src/main/AndroidManifest.xml +24 -0
- package/android/src/main/java/co/tryterra/sdk/reactnative/TerraBridgeModule.kt +96 -0
- package/android/src/main/java/co/tryterra/sdk/reactnative/TerraReactNativePackage.kt +26 -0
- package/android/src/main/java/co/tryterra/sdk/reactnative/TerraStartup.kt +46 -0
- package/android/src/samsung/kotlin/co/tryterra/sdk/reactnative/TerraStores.kt +13 -0
- package/docs/availability.md +396 -0
- package/frameworks/TerraSDK.xcframework/Info.plist +48 -0
- package/frameworks/TerraSDK.xcframework/ios-arm64/TerraSDK.framework/Info.plist +0 -0
- package/frameworks/TerraSDK.xcframework/ios-arm64/TerraSDK.framework/Modules/TerraSDK.swiftmodule/arm64-apple-ios.abi.json +82672 -0
- package/frameworks/TerraSDK.xcframework/ios-arm64/TerraSDK.framework/Modules/TerraSDK.swiftmodule/arm64-apple-ios.package.swiftinterface +2186 -0
- package/frameworks/TerraSDK.xcframework/ios-arm64/TerraSDK.framework/Modules/TerraSDK.swiftmodule/arm64-apple-ios.private.swiftinterface +2186 -0
- package/frameworks/TerraSDK.xcframework/ios-arm64/TerraSDK.framework/Modules/TerraSDK.swiftmodule/arm64-apple-ios.swiftdoc +0 -0
- package/frameworks/TerraSDK.xcframework/ios-arm64/TerraSDK.framework/Modules/TerraSDK.swiftmodule/arm64-apple-ios.swiftinterface +2163 -0
- package/frameworks/TerraSDK.xcframework/ios-arm64/TerraSDK.framework/PrivacyInfo.xcprivacy +96 -0
- package/frameworks/TerraSDK.xcframework/ios-arm64/TerraSDK.framework/TerraSDK +0 -0
- package/frameworks/TerraSDK.xcframework/ios-arm64/dSYMs/TerraSDK.framework.dSYM/Contents/Info.plist +20 -0
- package/frameworks/TerraSDK.xcframework/ios-arm64/dSYMs/TerraSDK.framework.dSYM/Contents/Resources/DWARF/TerraSDK +0 -0
- package/frameworks/TerraSDK.xcframework/ios-arm64/dSYMs/TerraSDK.framework.dSYM/Contents/Resources/Relocations/aarch64/TerraSDK.yml +6594 -0
- package/frameworks/TerraSDK.xcframework/ios-arm64_x86_64-simulator/TerraSDK.framework/Info.plist +0 -0
- package/frameworks/TerraSDK.xcframework/ios-arm64_x86_64-simulator/TerraSDK.framework/Modules/TerraSDK.swiftmodule/arm64-apple-ios-simulator.abi.json +82672 -0
- package/frameworks/TerraSDK.xcframework/ios-arm64_x86_64-simulator/TerraSDK.framework/Modules/TerraSDK.swiftmodule/arm64-apple-ios-simulator.package.swiftinterface +2186 -0
- package/frameworks/TerraSDK.xcframework/ios-arm64_x86_64-simulator/TerraSDK.framework/Modules/TerraSDK.swiftmodule/arm64-apple-ios-simulator.private.swiftinterface +2186 -0
- package/frameworks/TerraSDK.xcframework/ios-arm64_x86_64-simulator/TerraSDK.framework/Modules/TerraSDK.swiftmodule/arm64-apple-ios-simulator.swiftdoc +0 -0
- package/frameworks/TerraSDK.xcframework/ios-arm64_x86_64-simulator/TerraSDK.framework/Modules/TerraSDK.swiftmodule/arm64-apple-ios-simulator.swiftinterface +2163 -0
- package/frameworks/TerraSDK.xcframework/ios-arm64_x86_64-simulator/TerraSDK.framework/Modules/TerraSDK.swiftmodule/x86_64-apple-ios-simulator.abi.json +82672 -0
- package/frameworks/TerraSDK.xcframework/ios-arm64_x86_64-simulator/TerraSDK.framework/Modules/TerraSDK.swiftmodule/x86_64-apple-ios-simulator.package.swiftinterface +2186 -0
- package/frameworks/TerraSDK.xcframework/ios-arm64_x86_64-simulator/TerraSDK.framework/Modules/TerraSDK.swiftmodule/x86_64-apple-ios-simulator.private.swiftinterface +2186 -0
- package/frameworks/TerraSDK.xcframework/ios-arm64_x86_64-simulator/TerraSDK.framework/Modules/TerraSDK.swiftmodule/x86_64-apple-ios-simulator.swiftdoc +0 -0
- package/frameworks/TerraSDK.xcframework/ios-arm64_x86_64-simulator/TerraSDK.framework/Modules/TerraSDK.swiftmodule/x86_64-apple-ios-simulator.swiftinterface +2163 -0
- package/frameworks/TerraSDK.xcframework/ios-arm64_x86_64-simulator/TerraSDK.framework/PrivacyInfo.xcprivacy +96 -0
- package/frameworks/TerraSDK.xcframework/ios-arm64_x86_64-simulator/TerraSDK.framework/TerraSDK +0 -0
- package/frameworks/TerraSDK.xcframework/ios-arm64_x86_64-simulator/TerraSDK.framework/_CodeSignature/CodeResources +223 -0
- package/frameworks/TerraSDK.xcframework/ios-arm64_x86_64-simulator/dSYMs/TerraSDK.framework.dSYM/Contents/Info.plist +20 -0
- package/frameworks/TerraSDK.xcframework/ios-arm64_x86_64-simulator/dSYMs/TerraSDK.framework.dSYM/Contents/Resources/DWARF/TerraSDK +0 -0
- package/frameworks/TerraSDK.xcframework/ios-arm64_x86_64-simulator/dSYMs/TerraSDK.framework.dSYM/Contents/Resources/Relocations/aarch64/TerraSDK.yml +6595 -0
- package/frameworks/TerraSDK.xcframework/ios-arm64_x86_64-simulator/dSYMs/TerraSDK.framework.dSYM/Contents/Resources/Relocations/x86_64/TerraSDK.yml +6513 -0
- package/ios/TerraBridgeCore/BridgeBootstrap.swift +89 -0
- package/ios/TerraBridgeCore/BridgeHost.swift +13 -0
- package/ios/TerraBridgeCore/Conversions+Reads.swift +259 -0
- package/ios/TerraBridgeCore/Conversions.swift +594 -0
- package/ios/TerraBridgeCore/Generated/BridgeErrors.swift +179 -0
- package/ios/TerraBridgeCore/Generated/BridgeOps.swift +967 -0
- package/ios/TerraBridgeCore/Generated/BridgeTypes.swift +2050 -0
- package/ios/TerraBridgeCore/Locked.swift +15 -0
- package/ios/TerraBridgeCore/README.md +1 -0
- package/ios/TerraBridgeCore/Rejections.swift +130 -0
- package/ios/TerraBridgeCore/RuntimeAttachment.swift +60 -0
- package/ios/TerraBridgeCore/StreamRegistry.swift +59 -0
- package/ios/TerraBridgeCore/TerraBridge.swift +122 -0
- package/ios/TerraBridgeCore/TerraBridgeOpsImpl.swift +318 -0
- package/ios/TerraBridgeCore/TerraBridgeStreamsImpl.swift +60 -0
- package/ios/TerraBridgeCore/TokenBroker.swift +147 -0
- package/ios/TerraBridgeModule.mm +79 -0
- package/ios/TerraBridgeModuleImpl.swift +49 -0
- package/ios/TerraReactNative.swift +19 -0
- package/lib/EndUser.d.ts +12 -0
- package/lib/EndUser.js +13 -0
- package/lib/NativeTerraBridge.d.ts +23 -0
- package/lib/NativeTerraBridge.js +13 -0
- package/lib/Terra.d.ts +223 -0
- package/lib/Terra.js +336 -0
- package/lib/TerraError.d.ts +42 -0
- package/lib/TerraError.js +95 -0
- package/lib/createTerra.d.ts +8 -0
- package/lib/createTerra.js +19 -0
- package/lib/generated/client.d.ts +128 -0
- package/lib/generated/client.js +256 -0
- package/lib/generated/codecs.d.ts +219 -0
- package/lib/generated/codecs.js +2066 -0
- package/lib/generated/errors.d.ts +13 -0
- package/lib/generated/errors.js +68 -0
- package/lib/generated/ops.d.ts +13 -0
- package/lib/generated/ops.js +161 -0
- package/lib/generated/streams.d.ts +23 -0
- package/lib/generated/streams.js +58 -0
- package/lib/generated/types.d.ts +794 -0
- package/lib/generated/types.js +224 -0
- package/lib/index.d.ts +29 -0
- package/lib/index.js +55 -0
- package/package.json +71 -4
- package/src/EndUser.ts +15 -0
- package/src/NativeTerraBridge.ts +27 -0
- package/src/Terra.ts +592 -0
- package/src/TerraError.ts +116 -0
- package/src/createTerra.ts +18 -0
- package/src/generated/client.ts +315 -0
- package/src/generated/codecs.ts +1989 -0
- package/src/generated/errors.ts +113 -0
- package/src/generated/ops.ts +233 -0
- package/src/generated/streams.ts +74 -0
- package/src/generated/types.ts +1222 -0
- package/src/index.ts +54 -0
|
@@ -0,0 +1,557 @@
|
|
|
1
|
+
package co.tryterra.sdk.bridge
|
|
2
|
+
|
|
3
|
+
import co.tryterra.sdk.DataCategory
|
|
4
|
+
import co.tryterra.sdk.DietaryQuantity
|
|
5
|
+
import co.tryterra.sdk.MealSample
|
|
6
|
+
import co.tryterra.sdk.MenstruationFlowDay
|
|
7
|
+
import co.tryterra.sdk.MenstruationFlowSample
|
|
8
|
+
import co.tryterra.sdk.Nutrient
|
|
9
|
+
import co.tryterra.sdk.EndUser
|
|
10
|
+
import co.tryterra.sdk.BodyEntry
|
|
11
|
+
import co.tryterra.sdk.HeartRateEntry
|
|
12
|
+
import co.tryterra.sdk.NutritionEntry
|
|
13
|
+
import co.tryterra.sdk.HeartRateSample
|
|
14
|
+
import co.tryterra.sdk.PermissionStatus
|
|
15
|
+
import co.tryterra.sdk.RecordId
|
|
16
|
+
import co.tryterra.sdk.SampleInterval
|
|
17
|
+
import co.tryterra.sdk.SampleSource
|
|
18
|
+
import co.tryterra.sdk.SleepEntry
|
|
19
|
+
import co.tryterra.sdk.SleepSegment
|
|
20
|
+
import co.tryterra.sdk.SleepStage
|
|
21
|
+
import co.tryterra.sdk.Source
|
|
22
|
+
import co.tryterra.sdk.StepCount
|
|
23
|
+
import co.tryterra.sdk.StepEntry
|
|
24
|
+
import co.tryterra.sdk.TimeRange
|
|
25
|
+
import co.tryterra.sdk.Workout
|
|
26
|
+
import co.tryterra.sdk.WorkoutActivity
|
|
27
|
+
import co.tryterra.sdk.WorkoutEntry
|
|
28
|
+
import co.tryterra.sdk.WorkoutScope
|
|
29
|
+
import co.tryterra.sdk.bridge.generated.BridgeInstant
|
|
30
|
+
import co.tryterra.sdk.bridge.generated.BridgeError
|
|
31
|
+
import co.tryterra.sdk.diagnostics.BackgroundRestrictionReason
|
|
32
|
+
import co.tryterra.sdk.diagnostics.DiagnosticsState
|
|
33
|
+
import co.tryterra.sdk.diagnostics.DiagnosticsWire
|
|
34
|
+
import co.tryterra.sdk.diagnostics.SupportReport
|
|
35
|
+
import co.tryterra.sdk.diagnostics.TerraEvent
|
|
36
|
+
import co.tryterra.sdk.health.HealthAccess
|
|
37
|
+
import co.tryterra.sdk.HealthChange
|
|
38
|
+
import co.tryterra.sdk.store.StoredConnection
|
|
39
|
+
import java.time.ZoneId
|
|
40
|
+
import co.tryterra.sdk.bridge.generated.BackgroundRestrictionReason as WireRestriction
|
|
41
|
+
import co.tryterra.sdk.bridge.generated.BodyEntry as WireBodyEntry
|
|
42
|
+
import co.tryterra.sdk.bridge.generated.Connection as WireConnection
|
|
43
|
+
import co.tryterra.sdk.bridge.generated.NutritionEntry as WireNutritionEntry
|
|
44
|
+
import co.tryterra.sdk.bridge.generated.Nutrients as WireNutrients
|
|
45
|
+
import co.tryterra.sdk.bridge.generated.DataCategory as WireCategory
|
|
46
|
+
import co.tryterra.sdk.bridge.generated.DietaryQuantity as WireDietaryQuantity
|
|
47
|
+
import co.tryterra.sdk.bridge.generated.MealType as WireMealType
|
|
48
|
+
import co.tryterra.sdk.bridge.generated.MenstruationFlow as WireMenstruationFlow
|
|
49
|
+
import co.tryterra.sdk.bridge.generated.MenstruationFlowDay as WireMenstruationFlowDay
|
|
50
|
+
import co.tryterra.sdk.bridge.generated.Nutrient as WireNutrient
|
|
51
|
+
import co.tryterra.sdk.bridge.generated.DiagnosticsState as WireDiagnosticsState
|
|
52
|
+
import co.tryterra.sdk.bridge.generated.EndUser as WireEndUser
|
|
53
|
+
import co.tryterra.sdk.bridge.generated.HealthAccess as WireHealthAccess
|
|
54
|
+
import co.tryterra.sdk.bridge.generated.HealthChange as WireHealthChange
|
|
55
|
+
import co.tryterra.sdk.bridge.generated.HeartRateEntry as WireHeartRateEntry
|
|
56
|
+
import co.tryterra.sdk.bridge.generated.HeartRateSample as WireHeartRateSample
|
|
57
|
+
import co.tryterra.sdk.bridge.generated.MotionContext as WireMotionContext
|
|
58
|
+
import co.tryterra.sdk.bridge.generated.PermissionStatus as WirePermissionStatus
|
|
59
|
+
import co.tryterra.sdk.bridge.generated.SampleSource as WireSampleSource
|
|
60
|
+
import co.tryterra.sdk.bridge.generated.SleepEntry as WireSleepEntry
|
|
61
|
+
import co.tryterra.sdk.bridge.generated.SleepSegment as WireSleepSegment
|
|
62
|
+
import co.tryterra.sdk.bridge.generated.SleepStage as WireSleepStage
|
|
63
|
+
import co.tryterra.sdk.bridge.generated.Source as WireSource
|
|
64
|
+
import co.tryterra.sdk.bridge.generated.StepCount as WireStepCount
|
|
65
|
+
import co.tryterra.sdk.bridge.generated.StepEntry as WireStepEntry
|
|
66
|
+
import co.tryterra.sdk.bridge.generated.SupportReport as WireSupportReport
|
|
67
|
+
import co.tryterra.sdk.bridge.generated.TerraEvent as WireTerraEvent
|
|
68
|
+
import co.tryterra.sdk.bridge.generated.TimeRange as WireTimeRange
|
|
69
|
+
import co.tryterra.sdk.bridge.generated.Workout as WireWorkout
|
|
70
|
+
import co.tryterra.sdk.bridge.generated.WorkoutActivity as WireWorkoutActivity
|
|
71
|
+
import co.tryterra.sdk.bridge.generated.WorkoutEntry as WireWorkoutEntry
|
|
72
|
+
import co.tryterra.sdk.bridge.generated.WorkoutScope as WireWorkoutScope
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Between the SDK's types and the generated wire DTOs, in one place.
|
|
76
|
+
*
|
|
77
|
+
* Every `when` here is exhaustive over a sealed type or an enum with no `else`, which is what makes
|
|
78
|
+
* a case added to the SDK a compile error in this file rather than a value that quietly maps to
|
|
79
|
+
* something plausible. That is lock 1 of the four, and it is the lock the old wrapper did not have:
|
|
80
|
+
* `TerraReactModule.java` and `TerraReact.swift` each carried a `switch` with a fall-through, and a
|
|
81
|
+
* data type stopped travelling without anything going red.
|
|
82
|
+
*
|
|
83
|
+
* The SDK's models are deliberately not `@Serializable`. Keeping the wire shape out of the SDK's
|
|
84
|
+
* public types is the better boundary, and the price of it is this file.
|
|
85
|
+
*/
|
|
86
|
+
internal object Conversions {
|
|
87
|
+
|
|
88
|
+
// ---- enums ------------------------------------------------------------------------------
|
|
89
|
+
|
|
90
|
+
fun source(wire: WireSource): Source = when (wire) {
|
|
91
|
+
WireSource.HEALTH -> Source.HEALTH
|
|
92
|
+
WireSource.SAMSUNG_HEALTH -> Source.SAMSUNG_HEALTH
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
fun source(source: Source): WireSource = when (source) {
|
|
96
|
+
Source.HEALTH -> WireSource.HEALTH
|
|
97
|
+
Source.SAMSUNG_HEALTH -> WireSource.SAMSUNG_HEALTH
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* `ATHLETE` is HealthKit's characteristics and has no Health Connect counterpart, so a request
|
|
102
|
+
* naming it is refused as this platform lacking it rather than quietly dropped from the sheet.
|
|
103
|
+
*/
|
|
104
|
+
fun category(wire: WireCategory): DataCategory = when (wire) {
|
|
105
|
+
WireCategory.ACTIVITY -> DataCategory.ACTIVITY
|
|
106
|
+
WireCategory.BODY -> DataCategory.BODY
|
|
107
|
+
WireCategory.DAILY -> DataCategory.DAILY
|
|
108
|
+
WireCategory.SLEEP -> DataCategory.SLEEP
|
|
109
|
+
WireCategory.NUTRITION -> DataCategory.NUTRITION
|
|
110
|
+
WireCategory.MENSTRUATION -> DataCategory.MENSTRUATION
|
|
111
|
+
WireCategory.ATHLETE -> throw BridgeError.UnsupportedOnPlatform("athlete")
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
fun category(category: DataCategory): WireCategory = when (category) {
|
|
115
|
+
DataCategory.ACTIVITY -> WireCategory.ACTIVITY
|
|
116
|
+
DataCategory.BODY -> WireCategory.BODY
|
|
117
|
+
DataCategory.DAILY -> WireCategory.DAILY
|
|
118
|
+
DataCategory.SLEEP -> WireCategory.SLEEP
|
|
119
|
+
DataCategory.NUTRITION -> WireCategory.NUTRITION
|
|
120
|
+
DataCategory.MENSTRUATION -> WireCategory.MENSTRUATION
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* `UNAVAILABLE` is Apple's and has no Android counterpart, so nothing here can produce it.
|
|
125
|
+
*
|
|
126
|
+
* The surface types it as iOS-only for exactly that reason: a caller narrowing on it is writing
|
|
127
|
+
* iOS-specific code, and the type says so rather than letting them discover it.
|
|
128
|
+
*/
|
|
129
|
+
fun permissionStatus(status: PermissionStatus): WirePermissionStatus = when (status) {
|
|
130
|
+
PermissionStatus.GRANTED -> WirePermissionStatus.GRANTED
|
|
131
|
+
PermissionStatus.DENIED -> WirePermissionStatus.DENIED
|
|
132
|
+
PermissionStatus.NOT_DETERMINED -> WirePermissionStatus.NOT_DETERMINED
|
|
133
|
+
PermissionStatus.NOT_DISCLOSED -> WirePermissionStatus.NOT_DISCLOSED
|
|
134
|
+
PermissionStatus.UNSUPPORTED -> WirePermissionStatus.UNSUPPORTED
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
fun restriction(reason: BackgroundRestrictionReason): WireRestriction = when (reason) {
|
|
138
|
+
BackgroundRestrictionReason.OEM_RESTRICTED -> WireRestriction.OEM_RESTRICTED
|
|
139
|
+
BackgroundRestrictionReason.BACKGROUND_READ_DENIED -> WireRestriction.BACKGROUND_READ_DENIED
|
|
140
|
+
BackgroundRestrictionReason.HEALTH_CONNECT_FEATURE_UNAVAILABLE ->
|
|
141
|
+
WireRestriction.HEALTH_CONNECT_FEATURE_UNAVAILABLE
|
|
142
|
+
BackgroundRestrictionReason.STORE_FEATURE_UNAVAILABLE -> WireRestriction.STORE_FEATURE_UNAVAILABLE
|
|
143
|
+
BackgroundRestrictionReason.TICKS_NOT_RUNNING -> WireRestriction.TICKS_NOT_RUNNING
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// ---- unions -----------------------------------------------------------------------------
|
|
147
|
+
|
|
148
|
+
fun endUser(wire: WireEndUser): EndUser = when (wire) {
|
|
149
|
+
is WireEndUser.Reference -> EndUser.Reference(wire.id)
|
|
150
|
+
WireEndUser.Anonymous -> EndUser.Anonymous
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
fun timeRange(wire: WireTimeRange): TimeRange = when (wire) {
|
|
154
|
+
is WireTimeRange.LastHours -> TimeRange.LastHours(wire.hours)
|
|
155
|
+
is WireTimeRange.LastDays -> TimeRange.LastDays(wire.days)
|
|
156
|
+
WireTimeRange.Today -> TimeRange.Today
|
|
157
|
+
is WireTimeRange.Between -> TimeRange.Between(
|
|
158
|
+
start = BridgeInstant.parse(wire.start),
|
|
159
|
+
end = BridgeInstant.parse(wire.end),
|
|
160
|
+
)
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
fun sleepStage(stage: SleepStage): WireSleepStage = when (stage) {
|
|
164
|
+
SleepStage.InBed -> WireSleepStage.InBed
|
|
165
|
+
SleepStage.Awake -> WireSleepStage.Awake
|
|
166
|
+
SleepStage.Asleep -> WireSleepStage.Asleep
|
|
167
|
+
SleepStage.Light -> WireSleepStage.Light
|
|
168
|
+
SleepStage.Deep -> WireSleepStage.Deep
|
|
169
|
+
SleepStage.Rem -> WireSleepStage.Rem
|
|
170
|
+
is SleepStage.Unknown -> WireSleepStage.Unknown(stage.raw)
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
fun sleepStage(wire: WireSleepStage): SleepStage = when (wire) {
|
|
174
|
+
WireSleepStage.InBed -> SleepStage.InBed
|
|
175
|
+
WireSleepStage.Awake -> SleepStage.Awake
|
|
176
|
+
WireSleepStage.Asleep -> SleepStage.Asleep
|
|
177
|
+
WireSleepStage.Light -> SleepStage.Light
|
|
178
|
+
WireSleepStage.Deep -> SleepStage.Deep
|
|
179
|
+
WireSleepStage.Rem -> SleepStage.Rem
|
|
180
|
+
is WireSleepStage.Unknown -> SleepStage.Unknown(wire.raw)
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
fun activity(activity: WorkoutActivity): WireWorkoutActivity = when (activity) {
|
|
184
|
+
WorkoutActivity.Running -> WireWorkoutActivity.Running
|
|
185
|
+
WorkoutActivity.Walking -> WireWorkoutActivity.Walking
|
|
186
|
+
WorkoutActivity.Cycling -> WireWorkoutActivity.Cycling
|
|
187
|
+
WorkoutActivity.Swimming -> WireWorkoutActivity.Swimming
|
|
188
|
+
WorkoutActivity.StrengthTraining -> WireWorkoutActivity.StrengthTraining
|
|
189
|
+
is WorkoutActivity.Other -> WireWorkoutActivity.Other(activity.label)
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
fun activity(wire: WireWorkoutActivity): WorkoutActivity = when (wire) {
|
|
193
|
+
WireWorkoutActivity.Running -> WorkoutActivity.Running
|
|
194
|
+
WireWorkoutActivity.Walking -> WorkoutActivity.Walking
|
|
195
|
+
WireWorkoutActivity.Cycling -> WorkoutActivity.Cycling
|
|
196
|
+
WireWorkoutActivity.Swimming -> WorkoutActivity.Swimming
|
|
197
|
+
WireWorkoutActivity.StrengthTraining -> WorkoutActivity.StrengthTraining
|
|
198
|
+
is WireWorkoutActivity.Other -> WorkoutActivity.Other(wire.label)
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Android has no `devIDMismatch` state - it raises `dev_id_mismatch` instead - so nothing here
|
|
203
|
+
* produces that variant. The surface types it as iOS-only.
|
|
204
|
+
*/
|
|
205
|
+
fun diagnosticsState(state: DiagnosticsState): WireDiagnosticsState = when (state) {
|
|
206
|
+
DiagnosticsState.Healthy -> WireDiagnosticsState.Healthy
|
|
207
|
+
is DiagnosticsState.Quiet -> WireDiagnosticsState.Quiet(BridgeInstant.format(state.since))
|
|
208
|
+
is DiagnosticsState.BackgroundRestricted ->
|
|
209
|
+
WireDiagnosticsState.BackgroundRestricted(restriction(state.reason))
|
|
210
|
+
DiagnosticsState.BackgroundRefreshUnscheduled -> WireDiagnosticsState.BackgroundRefreshUnscheduled
|
|
211
|
+
DiagnosticsState.PendingRetry -> WireDiagnosticsState.PendingRetry
|
|
212
|
+
DiagnosticsState.LinkRevoked -> WireDiagnosticsState.LinkRevoked
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// ---- records ----------------------------------------------------------------------------
|
|
216
|
+
|
|
217
|
+
fun sampleSource(source: SampleSource): WireSampleSource = WireSampleSource(
|
|
218
|
+
name = source.name,
|
|
219
|
+
bundleId = source.bundleId,
|
|
220
|
+
productType = source.productType,
|
|
221
|
+
deviceType = source.deviceType,
|
|
222
|
+
)
|
|
223
|
+
|
|
224
|
+
fun connection(stored: StoredConnection): WireConnection = WireConnection(
|
|
225
|
+
source = source(stored.source),
|
|
226
|
+
userId = stored.userId,
|
|
227
|
+
providerUserId = stored.providerUserId,
|
|
228
|
+
)
|
|
229
|
+
|
|
230
|
+
fun heartRate(sample: HeartRateSample): WireHeartRateSample = WireHeartRateSample(
|
|
231
|
+
id = sample.id.value,
|
|
232
|
+
time = BridgeInstant.format(sample.time),
|
|
233
|
+
beatsPerMinute = sample.beatsPerMinute,
|
|
234
|
+
source = sampleSource(sample.source),
|
|
235
|
+
motionContext = sample.motionContext?.let {
|
|
236
|
+
when (it) {
|
|
237
|
+
co.tryterra.sdk.MotionContext.UNKNOWN -> WireMotionContext.UNKNOWN
|
|
238
|
+
co.tryterra.sdk.MotionContext.SEDENTARY -> WireMotionContext.SEDENTARY
|
|
239
|
+
co.tryterra.sdk.MotionContext.ACTIVE -> WireMotionContext.ACTIVE
|
|
240
|
+
}
|
|
241
|
+
},
|
|
242
|
+
userEntered = sample.userEntered,
|
|
243
|
+
timeZone = sample.timeZone?.id,
|
|
244
|
+
)
|
|
245
|
+
|
|
246
|
+
fun steps(sample: StepCount): WireStepCount = WireStepCount(
|
|
247
|
+
id = sample.id?.value,
|
|
248
|
+
start = BridgeInstant.format(sample.interval.start),
|
|
249
|
+
end = BridgeInstant.format(sample.interval.end),
|
|
250
|
+
steps = sample.steps,
|
|
251
|
+
source = sampleSource(sample.source),
|
|
252
|
+
userEntered = sample.userEntered,
|
|
253
|
+
timeZone = sample.timeZone?.id,
|
|
254
|
+
)
|
|
255
|
+
|
|
256
|
+
fun segment(segment: SleepSegment): WireSleepSegment = WireSleepSegment(
|
|
257
|
+
id = segment.id.value,
|
|
258
|
+
start = BridgeInstant.format(segment.interval.start),
|
|
259
|
+
end = BridgeInstant.format(segment.interval.end),
|
|
260
|
+
stage = sleepStage(segment.stage),
|
|
261
|
+
source = sampleSource(segment.source),
|
|
262
|
+
userEntered = segment.userEntered,
|
|
263
|
+
timeZone = segment.timeZone?.id,
|
|
264
|
+
)
|
|
265
|
+
|
|
266
|
+
fun nutrient(nutrient: Nutrient): WireNutrient = when (nutrient) {
|
|
267
|
+
Nutrient.ENERGY_CONSUMED -> WireNutrient.ENERGY_CONSUMED
|
|
268
|
+
Nutrient.PROTEIN -> WireNutrient.PROTEIN
|
|
269
|
+
Nutrient.CARBOHYDRATES -> WireNutrient.CARBOHYDRATES
|
|
270
|
+
Nutrient.FAT_TOTAL -> WireNutrient.FAT_TOTAL
|
|
271
|
+
Nutrient.FAT_SATURATED -> WireNutrient.FAT_SATURATED
|
|
272
|
+
Nutrient.FAT_MONOUNSATURATED -> WireNutrient.FAT_MONOUNSATURATED
|
|
273
|
+
Nutrient.FAT_POLYUNSATURATED -> WireNutrient.FAT_POLYUNSATURATED
|
|
274
|
+
Nutrient.SUGAR -> WireNutrient.SUGAR
|
|
275
|
+
Nutrient.FIBER -> WireNutrient.FIBER
|
|
276
|
+
Nutrient.CHOLESTEROL -> WireNutrient.CHOLESTEROL
|
|
277
|
+
Nutrient.SODIUM -> WireNutrient.SODIUM
|
|
278
|
+
Nutrient.POTASSIUM -> WireNutrient.POTASSIUM
|
|
279
|
+
Nutrient.CALCIUM -> WireNutrient.CALCIUM
|
|
280
|
+
Nutrient.IRON -> WireNutrient.IRON
|
|
281
|
+
Nutrient.MAGNESIUM -> WireNutrient.MAGNESIUM
|
|
282
|
+
Nutrient.ZINC -> WireNutrient.ZINC
|
|
283
|
+
Nutrient.PHOSPHORUS -> WireNutrient.PHOSPHORUS
|
|
284
|
+
Nutrient.IODINE -> WireNutrient.IODINE
|
|
285
|
+
Nutrient.MANGANESE -> WireNutrient.MANGANESE
|
|
286
|
+
Nutrient.MOLYBDENUM -> WireNutrient.MOLYBDENUM
|
|
287
|
+
Nutrient.SELENIUM -> WireNutrient.SELENIUM
|
|
288
|
+
Nutrient.COPPER -> WireNutrient.COPPER
|
|
289
|
+
Nutrient.CHROMIUM -> WireNutrient.CHROMIUM
|
|
290
|
+
Nutrient.CHLORIDE -> WireNutrient.CHLORIDE
|
|
291
|
+
Nutrient.CAFFEINE -> WireNutrient.CAFFEINE
|
|
292
|
+
Nutrient.VITAMIN_A -> WireNutrient.VITAMIN_A
|
|
293
|
+
Nutrient.VITAMIN_C -> WireNutrient.VITAMIN_C
|
|
294
|
+
Nutrient.VITAMIN_D -> WireNutrient.VITAMIN_D
|
|
295
|
+
Nutrient.VITAMIN_E -> WireNutrient.VITAMIN_E
|
|
296
|
+
Nutrient.VITAMIN_K -> WireNutrient.VITAMIN_K
|
|
297
|
+
Nutrient.VITAMIN_B6 -> WireNutrient.VITAMIN_B6
|
|
298
|
+
Nutrient.VITAMIN_B12 -> WireNutrient.VITAMIN_B12
|
|
299
|
+
Nutrient.THIAMIN -> WireNutrient.THIAMIN
|
|
300
|
+
Nutrient.RIBOFLAVIN -> WireNutrient.RIBOFLAVIN
|
|
301
|
+
Nutrient.NIACIN -> WireNutrient.NIACIN
|
|
302
|
+
Nutrient.FOLATE -> WireNutrient.FOLATE
|
|
303
|
+
Nutrient.BIOTIN -> WireNutrient.BIOTIN
|
|
304
|
+
Nutrient.PANTOTHENIC_ACID -> WireNutrient.PANTOTHENIC_ACID
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
fun mealType(type: MealSample.MealType): WireMealType = when (type) {
|
|
308
|
+
MealSample.MealType.BREAKFAST -> WireMealType.BREAKFAST
|
|
309
|
+
MealSample.MealType.LUNCH -> WireMealType.LUNCH
|
|
310
|
+
MealSample.MealType.DINNER -> WireMealType.DINNER
|
|
311
|
+
MealSample.MealType.SNACK -> WireMealType.SNACK
|
|
312
|
+
MealSample.MealType.UNKNOWN -> WireMealType.UNKNOWN
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
fun flow(flow: MenstruationFlowSample.Flow): WireMenstruationFlow = when (flow) {
|
|
316
|
+
MenstruationFlowSample.Flow.UNSPECIFIED -> WireMenstruationFlow.UNSPECIFIED
|
|
317
|
+
MenstruationFlowSample.Flow.LIGHT -> WireMenstruationFlow.LIGHT
|
|
318
|
+
MenstruationFlowSample.Flow.MEDIUM -> WireMenstruationFlow.MEDIUM
|
|
319
|
+
MenstruationFlowSample.Flow.HEAVY -> WireMenstruationFlow.HEAVY
|
|
320
|
+
MenstruationFlowSample.Flow.NONE -> WireMenstruationFlow.NO_FLOW
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
fun quantity(quantity: DietaryQuantity): WireDietaryQuantity = WireDietaryQuantity(
|
|
324
|
+
id = quantity.id.value,
|
|
325
|
+
nutrient = nutrient(quantity.nutrient),
|
|
326
|
+
value = quantity.value,
|
|
327
|
+
start = BridgeInstant.format(quantity.interval.start),
|
|
328
|
+
end = BridgeInstant.format(quantity.interval.end),
|
|
329
|
+
mealType = mealType(quantity.mealType),
|
|
330
|
+
name = quantity.name,
|
|
331
|
+
foodCorrelationId = quantity.foodCorrelationId?.value,
|
|
332
|
+
source = sampleSource(quantity.source),
|
|
333
|
+
userEntered = quantity.userEntered,
|
|
334
|
+
timeZone = quantity.timeZone?.id,
|
|
335
|
+
)
|
|
336
|
+
|
|
337
|
+
fun flowDay(day: MenstruationFlowDay): WireMenstruationFlowDay = WireMenstruationFlowDay(
|
|
338
|
+
id = day.id.value,
|
|
339
|
+
start = BridgeInstant.format(day.interval.start),
|
|
340
|
+
end = BridgeInstant.format(day.interval.end),
|
|
341
|
+
flow = flow(day.flow),
|
|
342
|
+
cycleStart = day.cycleStart,
|
|
343
|
+
source = sampleSource(day.source),
|
|
344
|
+
userEntered = day.userEntered,
|
|
345
|
+
timeZone = day.timeZone?.id,
|
|
346
|
+
)
|
|
347
|
+
|
|
348
|
+
fun workout(workout: Workout): WireWorkout = WireWorkout(
|
|
349
|
+
id = workout.id.value,
|
|
350
|
+
start = BridgeInstant.format(workout.interval.start),
|
|
351
|
+
end = BridgeInstant.format(workout.interval.end),
|
|
352
|
+
activity = activity(workout.activity),
|
|
353
|
+
activityTypeCode = workout.activityTypeCode,
|
|
354
|
+
indoor = workout.indoor,
|
|
355
|
+
activeEnergyKilocalories = workout.activeEnergyKilocalories,
|
|
356
|
+
distanceMeters = workout.distanceMeters,
|
|
357
|
+
source = sampleSource(workout.source),
|
|
358
|
+
userEntered = workout.userEntered,
|
|
359
|
+
timeZone = workout.timeZone?.id,
|
|
360
|
+
title = workout.title,
|
|
361
|
+
)
|
|
362
|
+
|
|
363
|
+
/**
|
|
364
|
+
* A change carries a day, not an instant.
|
|
365
|
+
*
|
|
366
|
+
* Sending the instant would make the receiving side re-derive the day in its own zone, and get
|
|
367
|
+
* a different one either side of midnight.
|
|
368
|
+
*/
|
|
369
|
+
fun change(change: HealthChange, zone: ZoneId): WireHealthChange =
|
|
370
|
+
WireHealthChange(category = category(change.category), day = change.day.atZone(zone).toLocalDate().toString())
|
|
371
|
+
|
|
372
|
+
/**
|
|
373
|
+
* An event is the object `DiagnosticsWire` already produces, carried unchanged.
|
|
374
|
+
*
|
|
375
|
+
* Not reshaped into a `{kind, payload}` of the wrapper's own: that would be a hand-written
|
|
376
|
+
* second contract here and another in the Swift half, sitting between the SDK and the generated
|
|
377
|
+
* codec where no vector can reach either of them. `EventPrivacyTest` guards this object against
|
|
378
|
+
* carrying a health value, and passing it through is what keeps that guarantee the only one.
|
|
379
|
+
*/
|
|
380
|
+
fun event(event: TerraEvent): WireTerraEvent = DiagnosticsWire.event(event)
|
|
381
|
+
|
|
382
|
+
fun report(report: SupportReport): WireSupportReport = WireSupportReport(
|
|
383
|
+
sdkVersion = report.sdkVersion,
|
|
384
|
+
generatedAt = BridgeInstant.format(report.generatedAt),
|
|
385
|
+
state = diagnosticsState(report.state),
|
|
386
|
+
lastDelivery = report.lastDelivery?.let { BridgeInstant.format(it) },
|
|
387
|
+
isConnected = report.isConnected,
|
|
388
|
+
requestedCategories = report.requestedCategories,
|
|
389
|
+
requestedWrites = report.requestedWrites,
|
|
390
|
+
requestedWorkouts = report.requestedWorkouts,
|
|
391
|
+
recentEvents = report.recentEvents.map(::event),
|
|
392
|
+
)
|
|
393
|
+
|
|
394
|
+
/**
|
|
395
|
+
* Sorted and merged, because a `Set` has no order and JSON has no set.
|
|
396
|
+
*
|
|
397
|
+
* Two scopes for one activity are one grant with both flags, not two entries: `HealthAccess`
|
|
398
|
+
* can hold `Running+energy` and `Running+distance` separately, and sending both would let a
|
|
399
|
+
* caller read a grant that contradicts itself depending on which it looked at first. Sorting is
|
|
400
|
+
* what makes two grants differing only in iteration order compare equal on the wire, without
|
|
401
|
+
* which the vectors would be flaky.
|
|
402
|
+
*/
|
|
403
|
+
fun access(access: HealthAccess): WireHealthAccess = WireHealthAccess(
|
|
404
|
+
read = access.read.map(::category).sortedBy { it.name },
|
|
405
|
+
write = access.write.map(::category).sortedBy { it.name },
|
|
406
|
+
workouts = access.workouts
|
|
407
|
+
.groupBy { WorkoutActivity.reportName(it.activity) }
|
|
408
|
+
.toSortedMap()
|
|
409
|
+
.map { (_, scopes) ->
|
|
410
|
+
WireWorkoutScope(
|
|
411
|
+
activity = activity(scopes.first().activity),
|
|
412
|
+
energy = scopes.any { it.energy },
|
|
413
|
+
distance = scopes.any { it.distance },
|
|
414
|
+
)
|
|
415
|
+
},
|
|
416
|
+
)
|
|
417
|
+
|
|
418
|
+
fun scope(scope: WorkoutScope): WireWorkoutScope = WireWorkoutScope(
|
|
419
|
+
activity = activity(scope.activity),
|
|
420
|
+
energy = scope.energy,
|
|
421
|
+
distance = scope.distance,
|
|
422
|
+
)
|
|
423
|
+
|
|
424
|
+
fun scope(wire: WireWorkoutScope): WorkoutScope = WorkoutScope(
|
|
425
|
+
activity = activity(wire.activity),
|
|
426
|
+
energy = wire.energy,
|
|
427
|
+
distance = wire.distance,
|
|
428
|
+
)
|
|
429
|
+
|
|
430
|
+
// ---- write entries ----------------------------------------------------------------------
|
|
431
|
+
|
|
432
|
+
/**
|
|
433
|
+
* A caller-supplied record id is honoured; an absent one is minted by the SDK's own default.
|
|
434
|
+
*
|
|
435
|
+
* Minting one here would use a different scheme from `RecordHandles.mint`, and `delete` only
|
|
436
|
+
* accepts handles that scheme produced.
|
|
437
|
+
*/
|
|
438
|
+
fun entry(wire: WireHeartRateEntry): HeartRateEntry =
|
|
439
|
+
wire.recordId?.let { HeartRateEntry(BridgeInstant.parse(wire.time), wire.beatsPerMinute, RecordId(it)) }
|
|
440
|
+
?: HeartRateEntry(BridgeInstant.parse(wire.time), wire.beatsPerMinute)
|
|
441
|
+
|
|
442
|
+
fun entry(wire: WireStepEntry): StepEntry {
|
|
443
|
+
val interval = SampleInterval(BridgeInstant.parse(wire.start), BridgeInstant.parse(wire.end))
|
|
444
|
+
return wire.recordId?.let { StepEntry(interval, wire.steps, RecordId(it)) }
|
|
445
|
+
?: StepEntry(interval, wire.steps)
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
fun entry(wire: WireSleepEntry): SleepEntry {
|
|
449
|
+
val interval = SampleInterval(BridgeInstant.parse(wire.start), BridgeInstant.parse(wire.end))
|
|
450
|
+
val stage = sleepStage(wire.stage)
|
|
451
|
+
return wire.recordId?.let { SleepEntry(interval, stage, RecordId(it)) } ?: SleepEntry(interval, stage)
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
fun entry(wire: WireWorkoutEntry): WorkoutEntry {
|
|
455
|
+
val interval = SampleInterval(BridgeInstant.parse(wire.start), BridgeInstant.parse(wire.end))
|
|
456
|
+
val activity = activity(wire.activity)
|
|
457
|
+
return wire.recordId?.let {
|
|
458
|
+
WorkoutEntry(interval, activity, wire.totalEnergyKilocalories, wire.totalDistanceMeters, RecordId(it))
|
|
459
|
+
} ?: WorkoutEntry(interval, activity, wire.totalEnergyKilocalories, wire.totalDistanceMeters)
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
/**
|
|
463
|
+
* Ten optional measurements and a time, one for one with `BodyEntry`.
|
|
464
|
+
*
|
|
465
|
+
* Spelled out rather than reflected: a field added to `BodyEntry` should be a decision here,
|
|
466
|
+
* and reflection would carry it silently with whatever name the SDK happened to give it.
|
|
467
|
+
*/
|
|
468
|
+
fun entry(wire: WireBodyEntry): BodyEntry {
|
|
469
|
+
val time = BridgeInstant.parse(wire.time)
|
|
470
|
+
return wire.recordId?.let {
|
|
471
|
+
BodyEntry(
|
|
472
|
+
time = time,
|
|
473
|
+
bodyMassKilograms = wire.bodyMassKilograms,
|
|
474
|
+
bodyFatPercent = wire.bodyFatPercent,
|
|
475
|
+
bodyMassIndex = wire.bodyMassIndex,
|
|
476
|
+
heightMeters = wire.heightMeters,
|
|
477
|
+
leanBodyMassKilograms = wire.leanBodyMassKilograms,
|
|
478
|
+
bodyTemperatureCelsius = wire.bodyTemperatureCelsius,
|
|
479
|
+
oxygenSaturationPercent = wire.oxygenSaturationPercent,
|
|
480
|
+
bloodGlucoseMilligramsPerDeciliter = wire.bloodGlucoseMilligramsPerDeciliter,
|
|
481
|
+
bloodPressureSystolicMillimetersOfMercury = wire.bloodPressureSystolicMillimetersOfMercury,
|
|
482
|
+
bloodPressureDiastolicMillimetersOfMercury = wire.bloodPressureDiastolicMillimetersOfMercury,
|
|
483
|
+
recordId = RecordId(it),
|
|
484
|
+
)
|
|
485
|
+
} ?: BodyEntry(
|
|
486
|
+
time = time,
|
|
487
|
+
bodyMassKilograms = wire.bodyMassKilograms,
|
|
488
|
+
bodyFatPercent = wire.bodyFatPercent,
|
|
489
|
+
bodyMassIndex = wire.bodyMassIndex,
|
|
490
|
+
heightMeters = wire.heightMeters,
|
|
491
|
+
leanBodyMassKilograms = wire.leanBodyMassKilograms,
|
|
492
|
+
bodyTemperatureCelsius = wire.bodyTemperatureCelsius,
|
|
493
|
+
oxygenSaturationPercent = wire.oxygenSaturationPercent,
|
|
494
|
+
bloodGlucoseMilligramsPerDeciliter = wire.bloodGlucoseMilligramsPerDeciliter,
|
|
495
|
+
bloodPressureSystolicMillimetersOfMercury = wire.bloodPressureSystolicMillimetersOfMercury,
|
|
496
|
+
bloodPressureDiastolicMillimetersOfMercury = wire.bloodPressureDiastolicMillimetersOfMercury,
|
|
497
|
+
)
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
fun entry(wire: WireNutritionEntry): NutritionEntry {
|
|
501
|
+
val interval = SampleInterval(BridgeInstant.parse(wire.start), BridgeInstant.parse(wire.end))
|
|
502
|
+
val nutrients = nutrients(wire.nutrients)
|
|
503
|
+
return wire.recordId?.let { NutritionEntry(interval, wire.name, nutrients, RecordId(it)) }
|
|
504
|
+
?: NutritionEntry(interval, wire.name, nutrients)
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
/**
|
|
508
|
+
* All thirty-nine, by name.
|
|
509
|
+
*
|
|
510
|
+
* The unit is in every name on both sides, so a mismatched pair is a compile error rather than
|
|
511
|
+
* a value that arrives in the wrong unit - which is the class of bug that would be invisible in
|
|
512
|
+
* every test that did not know what the number should be.
|
|
513
|
+
*/
|
|
514
|
+
private fun nutrients(wire: WireNutrients): NutritionEntry.Nutrients = NutritionEntry.Nutrients(
|
|
515
|
+
energyKilocalories = wire.energyKilocalories,
|
|
516
|
+
proteinGrams = wire.proteinGrams,
|
|
517
|
+
carbohydratesGrams = wire.carbohydratesGrams,
|
|
518
|
+
fiberGrams = wire.fiberGrams,
|
|
519
|
+
sugarGrams = wire.sugarGrams,
|
|
520
|
+
fatTotalGrams = wire.fatTotalGrams,
|
|
521
|
+
fatSaturatedGrams = wire.fatSaturatedGrams,
|
|
522
|
+
fatMonounsaturatedGrams = wire.fatMonounsaturatedGrams,
|
|
523
|
+
fatPolyunsaturatedGrams = wire.fatPolyunsaturatedGrams,
|
|
524
|
+
cholesterolMilligrams = wire.cholesterolMilligrams,
|
|
525
|
+
sodiumMilligrams = wire.sodiumMilligrams,
|
|
526
|
+
potassiumMilligrams = wire.potassiumMilligrams,
|
|
527
|
+
calciumMilligrams = wire.calciumMilligrams,
|
|
528
|
+
ironMilligrams = wire.ironMilligrams,
|
|
529
|
+
magnesiumMilligrams = wire.magnesiumMilligrams,
|
|
530
|
+
zincMilligrams = wire.zincMilligrams,
|
|
531
|
+
waterLiters = wire.waterLiters,
|
|
532
|
+
caffeineMilligrams = wire.caffeineMilligrams,
|
|
533
|
+
vitaminAMicrograms = wire.vitaminAMicrograms,
|
|
534
|
+
vitaminCMilligrams = wire.vitaminCMilligrams,
|
|
535
|
+
vitaminDMicrograms = wire.vitaminDMicrograms,
|
|
536
|
+
vitaminEMilligrams = wire.vitaminEMilligrams,
|
|
537
|
+
vitaminKMicrograms = wire.vitaminKMicrograms,
|
|
538
|
+
vitaminB6Milligrams = wire.vitaminB6Milligrams,
|
|
539
|
+
vitaminB12Micrograms = wire.vitaminB12Micrograms,
|
|
540
|
+
folateMicrograms = wire.folateMicrograms,
|
|
541
|
+
thiaminMilligrams = wire.thiaminMilligrams,
|
|
542
|
+
riboflavinMilligrams = wire.riboflavinMilligrams,
|
|
543
|
+
niacinMilligrams = wire.niacinMilligrams,
|
|
544
|
+
biotinMicrograms = wire.biotinMicrograms,
|
|
545
|
+
pantothenicAcidMilligrams = wire.pantothenicAcidMilligrams,
|
|
546
|
+
phosphorusMilligrams = wire.phosphorusMilligrams,
|
|
547
|
+
iodineMicrograms = wire.iodineMicrograms,
|
|
548
|
+
chromiumMicrograms = wire.chromiumMicrograms,
|
|
549
|
+
copperMilligrams = wire.copperMilligrams,
|
|
550
|
+
manganeseMilligrams = wire.manganeseMilligrams,
|
|
551
|
+
molybdenumMicrograms = wire.molybdenumMicrograms,
|
|
552
|
+
seleniumMicrograms = wire.seleniumMicrograms,
|
|
553
|
+
chlorideMilligrams = wire.chlorideMilligrams,
|
|
554
|
+
)
|
|
555
|
+
|
|
556
|
+
|
|
557
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
package co.tryterra.sdk.bridge
|
|
2
|
+
|
|
3
|
+
import co.tryterra.sdk.Source
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Which activity's launcher presents a permission sheet: the most recently started one that is
|
|
7
|
+
* still started.
|
|
8
|
+
*
|
|
9
|
+
* Creation order is not that. An app with two tasks, or one that returns to an older activity,
|
|
10
|
+
* keeps a newer activity alive but stopped, and a sheet launched through it would not be seen.
|
|
11
|
+
*/
|
|
12
|
+
internal class LauncherSlots<A : Any, L : Any> {
|
|
13
|
+
private val made = HashMap<A, Map<Source, L>>()
|
|
14
|
+
private val started = LinkedHashSet<A>()
|
|
15
|
+
|
|
16
|
+
@Synchronized
|
|
17
|
+
fun created(activity: A, launchers: Map<Source, L>) {
|
|
18
|
+
made[activity] = launchers
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
@Synchronized
|
|
22
|
+
fun started(activity: A) {
|
|
23
|
+
started.remove(activity)
|
|
24
|
+
started.add(activity)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
@Synchronized
|
|
28
|
+
fun stopped(activity: A) {
|
|
29
|
+
started.remove(activity)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** Forgets [activity], answering the launchers it held so the caller can unregister them. */
|
|
33
|
+
@Synchronized
|
|
34
|
+
fun destroyed(activity: A): Map<Source, L>? {
|
|
35
|
+
started.remove(activity)
|
|
36
|
+
return made.remove(activity)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
@Synchronized
|
|
40
|
+
fun current(source: Source): L? = started.toList().asReversed().firstNotNullOfOrNull { made[it]?.get(source) }
|
|
41
|
+
}
|