@cariva-dev/exercise-sdk 2.0.0
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/CarivaExerciseSdk.podspec +20 -0
- package/README.md +374 -0
- package/android/build.gradle +66 -0
- package/android/src/main/AndroidManifest.xml +40 -0
- package/android/src/main/java/com/carivaexercisesdk/CarivaExerciseSdkPackage.kt +30 -0
- package/android/src/main/java/com/carivaexercisesdk/HealthConnectModule.kt +222 -0
- package/android/src/main/java/com/carivaexercisesdk/HealthConnectPermissionUsageActivity.kt +11 -0
- package/android/src/main/java/com/carivaexercisesdk/HealthConnectPermissionsRationaleActivity.kt +11 -0
- package/android/src/main/java/com/carivaexercisesdk/Pagination.kt +23 -0
- package/android/src/main/java/com/carivaexercisesdk/application/connection/command/ConnectCommand.kt +12 -0
- package/android/src/main/java/com/carivaexercisesdk/application/connection/dto/ConnectResultDto.kt +13 -0
- package/android/src/main/java/com/carivaexercisesdk/application/connection/handler/ConnectHandler.kt +79 -0
- package/android/src/main/java/com/carivaexercisesdk/application/connection/port/HealthConnectConnectionPort.kt +16 -0
- package/android/src/main/java/com/carivaexercisesdk/application/connection/port/PermissionRequestPort.kt +5 -0
- package/android/src/main/java/com/carivaexercisesdk/application/datasource/command/SetDatasourcePolicyCommand.kt +6 -0
- package/android/src/main/java/com/carivaexercisesdk/application/datasource/handler/GetDatasourcePolicyHandler.kt +10 -0
- package/android/src/main/java/com/carivaexercisesdk/application/datasource/handler/SetDatasourcePolicyHandler.kt +22 -0
- package/android/src/main/java/com/carivaexercisesdk/application/datasource/port/DatasourcePolicyRepository.kt +9 -0
- package/android/src/main/java/com/carivaexercisesdk/application/exercise/dto/BasalReadResultDto.kt +11 -0
- package/android/src/main/java/com/carivaexercisesdk/application/exercise/dto/ExerciseDataDto.kt +41 -0
- package/android/src/main/java/com/carivaexercisesdk/application/exercise/handler/GetExerciseDataHandler.kt +346 -0
- package/android/src/main/java/com/carivaexercisesdk/application/exercise/port/ExerciseRecordPort.kt +45 -0
- package/android/src/main/java/com/carivaexercisesdk/application/exercise/query/GetExerciseDataQuery.kt +9 -0
- package/android/src/main/java/com/carivaexercisesdk/domain/connection/entity/ConnectionAggregate.kt +13 -0
- package/android/src/main/java/com/carivaexercisesdk/domain/connection/service/ConnectionDecisionService.kt +37 -0
- package/android/src/main/java/com/carivaexercisesdk/domain/connection/valueobject/ConnectionNextAction.kt +8 -0
- package/android/src/main/java/com/carivaexercisesdk/domain/connection/valueobject/HealthConnectStatus.kt +8 -0
- package/android/src/main/java/com/carivaexercisesdk/domain/connection/valueobject/PermissionScope.kt +36 -0
- package/android/src/main/java/com/carivaexercisesdk/domain/datasource/entity/DatasourcePolicy.kt +8 -0
- package/android/src/main/java/com/carivaexercisesdk/domain/datasource/valueobject/DatasourceType.kt +14 -0
- package/android/src/main/java/com/carivaexercisesdk/domain/exercise/services/ExerciseDomainService.kt +89 -0
- package/android/src/main/java/com/carivaexercisesdk/domain/exercise/services/HealthConnectCaloriesFallbackMerger.kt +351 -0
- package/android/src/main/java/com/carivaexercisesdk/domain/exercise/services/HealthConnectInternalsConstants.kt +55 -0
- package/android/src/main/java/com/carivaexercisesdk/domain/exercise/services/HealthConnectModuleInternals.kt +316 -0
- package/android/src/main/java/com/carivaexercisesdk/domain/exercise/services/HealthConnectOverlapNormalizer.kt +400 -0
- package/android/src/main/java/com/carivaexercisesdk/domain/exercise/services/HealthConnectSourceTrust.kt +249 -0
- package/android/src/main/java/com/carivaexercisesdk/domain/exercise/services/HealthConnectUnifiedRecordBuilder.kt +316 -0
- package/android/src/main/java/com/carivaexercisesdk/infrastructure/healthconnect/HealthConnectConnectionAdapter.kt +156 -0
- package/android/src/main/java/com/carivaexercisesdk/infrastructure/healthconnect/HealthConnectExerciseRecordAdapter.kt +464 -0
- package/android/src/main/java/com/carivaexercisesdk/infrastructure/persistence/InMemoryDatasourcePolicyRepository.kt +21 -0
- package/android/src/main/java/com/carivaexercisesdk/infrastructure/reactnative/ConnectResultMapper.kt +23 -0
- package/android/src/main/java/com/carivaexercisesdk/infrastructure/reactnative/DatasourcePolicyJsonParser.kt +51 -0
- package/android/src/main/java/com/carivaexercisesdk/infrastructure/reactnative/DatasourcePolicyMapper.kt +46 -0
- package/android/src/main/java/com/carivaexercisesdk/infrastructure/reactnative/ExerciseDataMapper.kt +99 -0
- package/android/src/test/java/com/carivaexercisesdk/ArchitectureDependencyRuleTest.kt +60 -0
- package/android/src/test/java/com/carivaexercisesdk/HealthConnectModuleDatasourceParserTest.kt +69 -0
- package/android/src/test/java/com/carivaexercisesdk/HealthConnectModuleInternalsTest.kt +406 -0
- package/android/src/test/java/com/carivaexercisesdk/application/connection/handler/ConnectHandlerTest.kt +153 -0
- package/android/src/test/java/com/carivaexercisesdk/application/datasource/handler/DatasourcePolicyRoundTripTest.kt +63 -0
- package/android/src/test/java/com/carivaexercisesdk/application/datasource/handler/SetDatasourcePolicyHandlerTest.kt +42 -0
- package/android/src/test/java/com/carivaexercisesdk/domain/connection/service/ConnectionDecisionServiceTest.kt +68 -0
- package/android/src/test/java/com/carivaexercisesdk/infrastructure/reactnative/DatasourcePolicyMapperTest.kt +22 -0
- package/ios/CarivaExerciseSdk.h +5 -0
- package/ios/CarivaExerciseSdk.mm +7 -0
- package/lib/module/connect/index.js +7 -0
- package/lib/module/connect/index.js.map +1 -0
- package/lib/module/datasource/index.js +10 -0
- package/lib/module/datasource/index.js.map +1 -0
- package/lib/module/exercise/index.js +7 -0
- package/lib/module/exercise/index.js.map +1 -0
- package/lib/module/index.js +6 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/native/module.js +13 -0
- package/lib/module/native/module.js.map +1 -0
- package/lib/module/package.json +1 -0
- package/lib/typescript/package.json +1 -0
- package/lib/typescript/src/connect/index.d.ts +16 -0
- package/lib/typescript/src/connect/index.d.ts.map +1 -0
- package/lib/typescript/src/datasource/index.d.ts +12 -0
- package/lib/typescript/src/datasource/index.d.ts.map +1 -0
- package/lib/typescript/src/exercise/index.d.ts +64 -0
- package/lib/typescript/src/exercise/index.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts +4 -0
- package/lib/typescript/src/index.d.ts.map +1 -0
- package/lib/typescript/src/native/module.d.ts +14 -0
- package/lib/typescript/src/native/module.d.ts.map +1 -0
- package/package.json +127 -0
- package/src/connect/index.ts +34 -0
- package/src/datasource/index.ts +20 -0
- package/src/exercise/index.ts +75 -0
- package/src/index.tsx +22 -0
- package/src/native/module.ts +23 -0
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
package com.carivaexercisesdk.application.connection.handler
|
|
2
|
+
|
|
3
|
+
import com.carivaexercisesdk.application.connection.command.ConnectCommand
|
|
4
|
+
import com.carivaexercisesdk.application.connection.command.MissingAppBehavior
|
|
5
|
+
import com.carivaexercisesdk.application.connection.port.HealthConnectConnectionPort
|
|
6
|
+
import com.carivaexercisesdk.application.connection.port.PermissionRequestPort
|
|
7
|
+
import com.carivaexercisesdk.domain.connection.valueobject.ConnectionNextAction
|
|
8
|
+
import com.carivaexercisesdk.domain.connection.valueobject.HealthConnectStatus
|
|
9
|
+
import com.carivaexercisesdk.domain.connection.valueobject.PermissionScope
|
|
10
|
+
import kotlinx.coroutines.runBlocking
|
|
11
|
+
import org.junit.Assert.assertEquals
|
|
12
|
+
import org.junit.Assert.assertFalse
|
|
13
|
+
import org.junit.Assert.assertTrue
|
|
14
|
+
import org.junit.Test
|
|
15
|
+
|
|
16
|
+
class ConnectHandlerTest {
|
|
17
|
+
|
|
18
|
+
@Test
|
|
19
|
+
fun handle_requestPermissionFalse_doesNotLaunchPermissionDialog() = runBlocking {
|
|
20
|
+
val connectionPort =
|
|
21
|
+
FakeConnectionPort(
|
|
22
|
+
status = HealthConnectStatus.AVAILABLE,
|
|
23
|
+
installed = true,
|
|
24
|
+
grantedPermissions = mutableSetOf()
|
|
25
|
+
)
|
|
26
|
+
val permissionPort = FakePermissionRequestPort()
|
|
27
|
+
val handler = ConnectHandler(connectionPort, permissionPort)
|
|
28
|
+
|
|
29
|
+
val result =
|
|
30
|
+
handler.handle(
|
|
31
|
+
ConnectCommand(
|
|
32
|
+
requestPermission = false,
|
|
33
|
+
permissionScope = listOf("steps")
|
|
34
|
+
)
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
assertEquals(ConnectionNextAction.REQUEST_PERMISSION, result.nextAction)
|
|
38
|
+
assertEquals(0, permissionPort.requestCalls)
|
|
39
|
+
assertFalse(result.ready)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
@Test
|
|
43
|
+
fun handle_requestPermissionTrue_requestsAndUsesLatestGrantState() = runBlocking {
|
|
44
|
+
val connectionPort =
|
|
45
|
+
FakeConnectionPort(
|
|
46
|
+
status = HealthConnectStatus.AVAILABLE,
|
|
47
|
+
installed = true,
|
|
48
|
+
grantedPermissions = mutableSetOf()
|
|
49
|
+
)
|
|
50
|
+
val permissionPort =
|
|
51
|
+
FakePermissionRequestPort {
|
|
52
|
+
connectionPort.grantedPermissions += "perm.steps"
|
|
53
|
+
}
|
|
54
|
+
val handler = ConnectHandler(connectionPort, permissionPort)
|
|
55
|
+
|
|
56
|
+
val result =
|
|
57
|
+
handler.handle(
|
|
58
|
+
ConnectCommand(
|
|
59
|
+
requestPermission = true,
|
|
60
|
+
permissionScope = listOf("steps")
|
|
61
|
+
)
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
assertEquals(1, permissionPort.requestCalls)
|
|
65
|
+
assertEquals(ConnectionNextAction.NONE, result.nextAction)
|
|
66
|
+
assertTrue(result.ready)
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
@Test
|
|
70
|
+
fun handle_openMode_missingHealthConnect_opensHealthConnectStore() = runBlocking {
|
|
71
|
+
val connectionPort =
|
|
72
|
+
FakeConnectionPort(
|
|
73
|
+
status = HealthConnectStatus.UNAVAILABLE,
|
|
74
|
+
installed = false,
|
|
75
|
+
grantedPermissions = mutableSetOf()
|
|
76
|
+
)
|
|
77
|
+
val permissionPort = FakePermissionRequestPort()
|
|
78
|
+
val handler = ConnectHandler(connectionPort, permissionPort)
|
|
79
|
+
|
|
80
|
+
val result =
|
|
81
|
+
handler.handle(
|
|
82
|
+
ConnectCommand(
|
|
83
|
+
onMissingApp = MissingAppBehavior.OPEN,
|
|
84
|
+
requestPermission = false,
|
|
85
|
+
permissionScope = listOf("steps")
|
|
86
|
+
)
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
assertTrue(connectionPort.openStoreCalled)
|
|
90
|
+
assertEquals(ConnectionNextAction.INSTALL_HEALTH_CONNECT, result.nextAction)
|
|
91
|
+
assertFalse(result.ready)
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
@Test
|
|
95
|
+
fun handle_invalidPermissionScope_throwsValidationError() = runBlocking {
|
|
96
|
+
val connectionPort =
|
|
97
|
+
FakeConnectionPort(
|
|
98
|
+
status = HealthConnectStatus.AVAILABLE,
|
|
99
|
+
installed = true,
|
|
100
|
+
grantedPermissions = mutableSetOf()
|
|
101
|
+
)
|
|
102
|
+
val permissionPort = FakePermissionRequestPort()
|
|
103
|
+
val handler = ConnectHandler(connectionPort, permissionPort)
|
|
104
|
+
|
|
105
|
+
try {
|
|
106
|
+
handler.handle(ConnectCommand(permissionScope = listOf("unknown_scope")))
|
|
107
|
+
} catch (e: InvalidPermissionScopeException) {
|
|
108
|
+
assertTrue(e.unsupportedScopes.contains("unknown_scope"))
|
|
109
|
+
return@runBlocking
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
throw AssertionError("Expected InvalidPermissionScopeException")
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
private class FakeConnectionPort(
|
|
116
|
+
val status: HealthConnectStatus,
|
|
117
|
+
val installed: Boolean,
|
|
118
|
+
val grantedPermissions: MutableSet<String>
|
|
119
|
+
) : HealthConnectConnectionPort {
|
|
120
|
+
var openStoreCalled: Boolean = false
|
|
121
|
+
|
|
122
|
+
override suspend fun getHealthConnectStatus(): HealthConnectStatus {
|
|
123
|
+
return status
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
override fun isHealthConnectInstalled(): Boolean {
|
|
127
|
+
return installed
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
override suspend fun getGrantedPermissions(): Set<String> {
|
|
131
|
+
return grantedPermissions
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
override fun resolvePermissions(scopes: Set<PermissionScope>): Set<String> {
|
|
135
|
+
return scopes.mapTo(mutableSetOf()) { "perm.${it.wireValue}" }
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
override fun openHealthConnectStore() {
|
|
139
|
+
openStoreCalled = true
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
private class FakePermissionRequestPort(
|
|
144
|
+
private val onRequest: () -> Unit = {}
|
|
145
|
+
) : PermissionRequestPort {
|
|
146
|
+
var requestCalls: Int = 0
|
|
147
|
+
|
|
148
|
+
override suspend fun requestPermissions(permissions: Set<String>) {
|
|
149
|
+
requestCalls += 1
|
|
150
|
+
onRequest()
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
package com.carivaexercisesdk.application.datasource.handler
|
|
2
|
+
|
|
3
|
+
import com.carivaexercisesdk.application.datasource.command.SetDatasourcePolicyCommand
|
|
4
|
+
import com.carivaexercisesdk.domain.datasource.valueobject.DatasourceType
|
|
5
|
+
import com.carivaexercisesdk.infrastructure.persistence.InMemoryDatasourcePolicyRepository
|
|
6
|
+
import com.carivaexercisesdk.infrastructure.reactnative.DatasourcePolicyJsonParser
|
|
7
|
+
import com.carivaexercisesdk.infrastructure.reactnative.DatasourcePolicyMapper
|
|
8
|
+
import org.junit.Assert.assertEquals
|
|
9
|
+
import org.junit.Test
|
|
10
|
+
|
|
11
|
+
class DatasourcePolicyRoundTripTest {
|
|
12
|
+
@Test
|
|
13
|
+
fun setThenGet_returnsCanonicalEquivalentConfig() {
|
|
14
|
+
val repository = InMemoryDatasourcePolicyRepository()
|
|
15
|
+
val handler = SetDatasourcePolicyHandler(repository)
|
|
16
|
+
val allowlist =
|
|
17
|
+
DatasourcePolicyJsonParser.resolveAllowlist(
|
|
18
|
+
hasAllowlistKey = true,
|
|
19
|
+
parsedAllowlist = listOf("DEVICE", " app ", "manual_inputx")
|
|
20
|
+
)
|
|
21
|
+
val scopePackages =
|
|
22
|
+
DatasourcePolicyJsonParser.resolveScopePackages(
|
|
23
|
+
listOf(" COM.FIT.APP ", "com.fit.app", "com.google.fit", "")
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
handler.handle(
|
|
27
|
+
SetDatasourcePolicyCommand(
|
|
28
|
+
allowlist = allowlist,
|
|
29
|
+
packageAllowlist = scopePackages
|
|
30
|
+
)
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
val canonical = DatasourcePolicyMapper.toCanonicalConfig(repository.get())
|
|
34
|
+
assertEquals(listOf("app", "device"), canonical.allowlist)
|
|
35
|
+
assertEquals(listOf("com.fit.app", "com.google.fit"), canonical.packages)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
@Test
|
|
39
|
+
fun setMissingAllowlist_defaultsToAllSources() {
|
|
40
|
+
val repository = InMemoryDatasourcePolicyRepository()
|
|
41
|
+
val handler = SetDatasourcePolicyHandler(repository)
|
|
42
|
+
val allowlist =
|
|
43
|
+
DatasourcePolicyJsonParser.resolveAllowlist(
|
|
44
|
+
hasAllowlistKey = false,
|
|
45
|
+
parsedAllowlist = emptyList()
|
|
46
|
+
)
|
|
47
|
+
val scopePackages = DatasourcePolicyJsonParser.resolveScopePackages(emptyList())
|
|
48
|
+
|
|
49
|
+
handler.handle(
|
|
50
|
+
SetDatasourcePolicyCommand(
|
|
51
|
+
allowlist = allowlist,
|
|
52
|
+
packageAllowlist = scopePackages
|
|
53
|
+
)
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
val canonical = DatasourcePolicyMapper.toCanonicalConfig(repository.get())
|
|
57
|
+
assertEquals(
|
|
58
|
+
DatasourceType.entries.map { it.wireValue }.sorted(),
|
|
59
|
+
canonical.allowlist
|
|
60
|
+
)
|
|
61
|
+
assertEquals(emptyList<String>(), canonical.packages)
|
|
62
|
+
}
|
|
63
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
package com.carivaexercisesdk.application.datasource.handler
|
|
2
|
+
|
|
3
|
+
import com.carivaexercisesdk.application.datasource.command.SetDatasourcePolicyCommand
|
|
4
|
+
import com.carivaexercisesdk.domain.datasource.valueobject.DatasourceType
|
|
5
|
+
import com.carivaexercisesdk.infrastructure.persistence.InMemoryDatasourcePolicyRepository
|
|
6
|
+
import org.junit.Assert.assertEquals
|
|
7
|
+
import org.junit.Test
|
|
8
|
+
|
|
9
|
+
class SetDatasourcePolicyHandlerTest {
|
|
10
|
+
@Test
|
|
11
|
+
fun handle_ignoresUnknownSources_andNormalizesPackageAllowlist() {
|
|
12
|
+
val handler = SetDatasourcePolicyHandler(InMemoryDatasourcePolicyRepository())
|
|
13
|
+
|
|
14
|
+
val policy =
|
|
15
|
+
handler.handle(
|
|
16
|
+
SetDatasourcePolicyCommand(
|
|
17
|
+
allowlist = listOf("DEVICE", " app ", "manual_inputx"),
|
|
18
|
+
packageAllowlist =
|
|
19
|
+
listOf(" com.Fit.App ", "COM.fit.app", "", "com.google.fit")
|
|
20
|
+
)
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
assertEquals(setOf(DatasourceType.DEVICE, DatasourceType.APP), policy.allowlist)
|
|
24
|
+
assertEquals(setOf("com.fit.app", "com.google.fit"), policy.packageAllowlist)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
@Test
|
|
28
|
+
fun handle_emptyAllowlist_keepsNoAllowedSources() {
|
|
29
|
+
val handler = SetDatasourcePolicyHandler(InMemoryDatasourcePolicyRepository())
|
|
30
|
+
|
|
31
|
+
val policy =
|
|
32
|
+
handler.handle(
|
|
33
|
+
SetDatasourcePolicyCommand(
|
|
34
|
+
allowlist = emptyList(),
|
|
35
|
+
packageAllowlist = emptyList()
|
|
36
|
+
)
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
assertEquals(emptySet<DatasourceType>(), policy.allowlist)
|
|
40
|
+
assertEquals(emptySet<String>(), policy.packageAllowlist)
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
package com.carivaexercisesdk.domain.connection.service
|
|
2
|
+
|
|
3
|
+
import com.carivaexercisesdk.domain.connection.valueobject.ConnectionNextAction
|
|
4
|
+
import com.carivaexercisesdk.domain.connection.valueobject.HealthConnectStatus
|
|
5
|
+
import org.junit.Assert.assertEquals
|
|
6
|
+
import org.junit.Assert.assertFalse
|
|
7
|
+
import org.junit.Assert.assertTrue
|
|
8
|
+
import org.junit.Test
|
|
9
|
+
|
|
10
|
+
class ConnectionDecisionServiceTest {
|
|
11
|
+
private val service = ConnectionDecisionService()
|
|
12
|
+
|
|
13
|
+
@Test
|
|
14
|
+
fun decide_healthConnectMissing_requestsInstall() {
|
|
15
|
+
val result =
|
|
16
|
+
service.decide(
|
|
17
|
+
healthConnectInstalled = false,
|
|
18
|
+
healthConnectStatus = HealthConnectStatus.UNAVAILABLE,
|
|
19
|
+
hasPermissions = false,
|
|
20
|
+
requestedPermissions = setOf("perm")
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
assertEquals(ConnectionNextAction.INSTALL_HEALTH_CONNECT, result.nextAction)
|
|
24
|
+
assertFalse(result.ready)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
@Test
|
|
28
|
+
fun decide_providerUpdateRequired_requestsUpdate() {
|
|
29
|
+
val result =
|
|
30
|
+
service.decide(
|
|
31
|
+
healthConnectInstalled = true,
|
|
32
|
+
healthConnectStatus = HealthConnectStatus.PROVIDER_UPDATE_REQUIRED,
|
|
33
|
+
hasPermissions = true,
|
|
34
|
+
requestedPermissions = emptySet()
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
assertEquals(ConnectionNextAction.UPDATE_HEALTH_CONNECT, result.nextAction)
|
|
38
|
+
assertFalse(result.ready)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
@Test
|
|
42
|
+
fun decide_missingPermissions_requestsPermission() {
|
|
43
|
+
val result =
|
|
44
|
+
service.decide(
|
|
45
|
+
healthConnectInstalled = true,
|
|
46
|
+
healthConnectStatus = HealthConnectStatus.AVAILABLE,
|
|
47
|
+
hasPermissions = false,
|
|
48
|
+
requestedPermissions = setOf("perm")
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
assertEquals(ConnectionNextAction.REQUEST_PERMISSION, result.nextAction)
|
|
52
|
+
assertFalse(result.ready)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
@Test
|
|
56
|
+
fun decide_availableAndPermitted_isReady() {
|
|
57
|
+
val result =
|
|
58
|
+
service.decide(
|
|
59
|
+
healthConnectInstalled = true,
|
|
60
|
+
healthConnectStatus = HealthConnectStatus.AVAILABLE,
|
|
61
|
+
hasPermissions = true,
|
|
62
|
+
requestedPermissions = setOf("perm")
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
assertEquals(ConnectionNextAction.NONE, result.nextAction)
|
|
66
|
+
assertTrue(result.ready)
|
|
67
|
+
}
|
|
68
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
package com.carivaexercisesdk.infrastructure.reactnative
|
|
2
|
+
|
|
3
|
+
import com.carivaexercisesdk.domain.datasource.entity.DatasourcePolicy
|
|
4
|
+
import com.carivaexercisesdk.domain.datasource.valueobject.DatasourceType
|
|
5
|
+
import org.junit.Assert.assertEquals
|
|
6
|
+
import org.junit.Test
|
|
7
|
+
|
|
8
|
+
class DatasourcePolicyMapperTest {
|
|
9
|
+
@Test
|
|
10
|
+
fun toCanonicalConfig_returnsCanonicalSortedAllowlistAndScopePackages() {
|
|
11
|
+
val policy =
|
|
12
|
+
DatasourcePolicy(
|
|
13
|
+
allowlist = setOf(DatasourceType.UNKNOWN, DatasourceType.DEVICE),
|
|
14
|
+
packageAllowlist = setOf("b.pkg", "a.pkg")
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
val config = DatasourcePolicyMapper.toCanonicalConfig(policy)
|
|
18
|
+
|
|
19
|
+
assertEquals(listOf("device", "unknown"), config.allowlist)
|
|
20
|
+
assertEquals(listOf("a.pkg", "b.pkg"), config.packages)
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["getHealthConnectModule","connect","options","JSON","stringify"],"sourceRoot":"../../../src","sources":["connect/index.ts"],"mappings":";;AAAA,SAASA,sBAAsB,QAAQ,qBAAkB;AA+BzD,OAAO,SAASC,OAAOA,CAACC,OAAuB,GAAG,CAAC,CAAC,EAA0B;EAC5E,OAAOF,sBAAsB,CAAC,CAAC,CAACC,OAAO,CAACE,IAAI,CAACC,SAAS,CAACF,OAAO,CAAC,CAAC;AAClE","ignoreList":[]}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
import { getHealthConnectModule } from "../native/module.js";
|
|
4
|
+
export function setAllowDatasource(config) {
|
|
5
|
+
return getHealthConnectModule().setAllowDatasource(JSON.stringify(config));
|
|
6
|
+
}
|
|
7
|
+
export function getAllowDatasource() {
|
|
8
|
+
return getHealthConnectModule().getAllowDatasource();
|
|
9
|
+
}
|
|
10
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["getHealthConnectModule","setAllowDatasource","config","JSON","stringify","getAllowDatasource"],"sourceRoot":"../../../src","sources":["datasource/index.ts"],"mappings":";;AAAA,SAASA,sBAAsB,QAAQ,qBAAkB;AAWzD,OAAO,SAASC,kBAAkBA,CAChCC,MAAwB,EACO;EAC/B,OAAOF,sBAAsB,CAAC,CAAC,CAACC,kBAAkB,CAACE,IAAI,CAACC,SAAS,CAACF,MAAM,CAAC,CAAC;AAC5E;AAEA,OAAO,SAASG,kBAAkBA,CAAA,EAA8B;EAC9D,OAAOL,sBAAsB,CAAC,CAAC,CAACK,kBAAkB,CAAC,CAAC;AACtD","ignoreList":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["getHealthConnectModule","getExerciseData","options","JSON","stringify"],"sourceRoot":"../../../src","sources":["exercise/index.ts"],"mappings":";;AAAA,SAASA,sBAAsB,QAAQ,qBAAkB;AAsEzD,OAAO,SAASC,eAAeA,CAC7BC,OAA+B,EACR;EACvB,OAAOF,sBAAsB,CAAC,CAAC,CAACC,eAAe,CAACE,IAAI,CAACC,SAAS,CAACF,OAAO,CAAC,CAAC;AAC1E","ignoreList":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["connect","getExerciseData","getAllowDatasource","setAllowDatasource"],"sourceRoot":"../../src","sources":["index.tsx"],"mappings":";;AAAA,SACEA,OAAO,QAIF,oBAAW;AAElB,SACEC,eAAe,QAMV,qBAAY;AAEnB,SACEC,kBAAkB,EAClBC,kBAAkB,QAGb,uBAAc","ignoreList":[]}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
import { NativeModules } from 'react-native';
|
|
4
|
+
const {
|
|
5
|
+
HealthConnectModule
|
|
6
|
+
} = NativeModules;
|
|
7
|
+
export function getHealthConnectModule() {
|
|
8
|
+
if (!HealthConnectModule) {
|
|
9
|
+
throw new Error('HealthConnectModule is not linked or unavailable.');
|
|
10
|
+
}
|
|
11
|
+
return HealthConnectModule;
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=module.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["NativeModules","HealthConnectModule","getHealthConnectModule","Error"],"sourceRoot":"../../../src","sources":["native/module.ts"],"mappings":";;AAAA,SAASA,aAAa,QAAQ,cAAc;AAa5C,MAAM;EAAEC;AAAoB,CAAC,GAAGD,aAE/B;AAED,OAAO,SAASE,sBAAsBA,CAAA,EAA4B;EAChE,IAAI,CAACD,mBAAmB,EAAE;IACxB,MAAM,IAAIE,KAAK,CAAC,mDAAmD,CAAC;EACtE;EACA,OAAOF,mBAAmB;AAC5B","ignoreList":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"type":"module"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"type":"module"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export type PermissionScope = 'steps' | 'activetimes' | 'calories' | 'distances';
|
|
2
|
+
export type ConnectOptions = {
|
|
3
|
+
onMissingApp?: 'status' | 'open';
|
|
4
|
+
requestPermission?: boolean;
|
|
5
|
+
permissionScope?: PermissionScope[];
|
|
6
|
+
};
|
|
7
|
+
export type ConnectResult = {
|
|
8
|
+
ready: boolean;
|
|
9
|
+
healthConnectInstalled: boolean;
|
|
10
|
+
healthConnectStatus: 'AVAILABLE' | 'PROVIDER_UPDATE_REQUIRED' | 'UNAVAILABLE' | 'UNKNOWN';
|
|
11
|
+
hasPermissions: boolean;
|
|
12
|
+
nextAction: 'INSTALL_HEALTH_CONNECT' | 'UPDATE_HEALTH_CONNECT' | 'REQUEST_PERMISSION' | 'NONE';
|
|
13
|
+
requestedPermissions?: string[];
|
|
14
|
+
};
|
|
15
|
+
export declare function connect(options?: ConnectOptions): Promise<ConnectResult>;
|
|
16
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/connect/index.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,eAAe,GACvB,OAAO,GACP,aAAa,GACb,UAAU,GACV,WAAW,CAAC;AAEhB,MAAM,MAAM,cAAc,GAAG;IAC3B,YAAY,CAAC,EAAE,QAAQ,GAAG,MAAM,CAAC;IACjC,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,eAAe,CAAC,EAAE,eAAe,EAAE,CAAC;CACrC,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG;IAC1B,KAAK,EAAE,OAAO,CAAC;IACf,sBAAsB,EAAE,OAAO,CAAC;IAChC,mBAAmB,EACf,WAAW,GACX,0BAA0B,GAC1B,aAAa,GACb,SAAS,CAAC;IACd,cAAc,EAAE,OAAO,CAAC;IACxB,UAAU,EACN,wBAAwB,GACxB,uBAAuB,GACvB,oBAAoB,GACpB,MAAM,CAAC;IACX,oBAAoB,CAAC,EAAE,MAAM,EAAE,CAAC;CACjC,CAAC;AAEF,wBAAgB,OAAO,CAAC,OAAO,GAAE,cAAmB,GAAG,OAAO,CAAC,aAAa,CAAC,CAE5E"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export type DatasourceType = 'manual_input' | 'device' | 'app' | 'unknown';
|
|
2
|
+
export type DatasourceConfig = {
|
|
3
|
+
allowlist: DatasourceType[];
|
|
4
|
+
scope?: {
|
|
5
|
+
packages?: string[];
|
|
6
|
+
};
|
|
7
|
+
};
|
|
8
|
+
export declare function setAllowDatasource(config: DatasourceConfig): Promise<{
|
|
9
|
+
success: boolean;
|
|
10
|
+
}>;
|
|
11
|
+
export declare function getAllowDatasource(): Promise<DatasourceConfig>;
|
|
12
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/datasource/index.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,cAAc,GAAG,cAAc,GAAG,QAAQ,GAAG,KAAK,GAAG,SAAS,CAAC;AAE3E,MAAM,MAAM,gBAAgB,GAAG;IAC7B,SAAS,EAAE,cAAc,EAAE,CAAC;IAC5B,KAAK,CAAC,EAAE;QACN,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;KACrB,CAAC;CACH,CAAC;AAEF,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,gBAAgB,GACvB,OAAO,CAAC;IAAE,OAAO,EAAE,OAAO,CAAA;CAAE,CAAC,CAE/B;AAED,wBAAgB,kBAAkB,IAAI,OAAO,CAAC,gBAAgB,CAAC,CAE9D"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
export type TrustLevel = 'trusted' | 'low_confidence';
|
|
2
|
+
export type UnifiedRecordMeta = {
|
|
3
|
+
source: string;
|
|
4
|
+
device: string;
|
|
5
|
+
deviceModel: string;
|
|
6
|
+
trustLevel: TrustLevel;
|
|
7
|
+
};
|
|
8
|
+
export type UnifiedIntervalRecord = {
|
|
9
|
+
steps: number;
|
|
10
|
+
activeCalories: number;
|
|
11
|
+
activeCaloriesUnit: 'kcal';
|
|
12
|
+
distance: number;
|
|
13
|
+
distanceUnit: 'km';
|
|
14
|
+
activeTime: number;
|
|
15
|
+
activeTimeUnit: 'milliseconds';
|
|
16
|
+
startDate: string;
|
|
17
|
+
endDate: string;
|
|
18
|
+
meta: UnifiedRecordMeta;
|
|
19
|
+
};
|
|
20
|
+
export type ExerciseData = {
|
|
21
|
+
totalKcal: number;
|
|
22
|
+
totalSteps: number;
|
|
23
|
+
totalActiveTimeMillis: number;
|
|
24
|
+
totalActiveTime?: number;
|
|
25
|
+
totalDistanceMeters: number;
|
|
26
|
+
timeZone: string;
|
|
27
|
+
records: UnifiedIntervalRecord[];
|
|
28
|
+
warnings?: string[];
|
|
29
|
+
integrity?: {
|
|
30
|
+
projectionPreserved: boolean;
|
|
31
|
+
roundingScale: number;
|
|
32
|
+
};
|
|
33
|
+
diagnostics?: {
|
|
34
|
+
fallbackUsedSegments: number;
|
|
35
|
+
fallbackSuppressedSegments: number;
|
|
36
|
+
idleGateSuppressedSegments: number;
|
|
37
|
+
basalSeedApplied: boolean;
|
|
38
|
+
seededFromBeforeSince: boolean;
|
|
39
|
+
basalSeedAgeHours?: number;
|
|
40
|
+
};
|
|
41
|
+
debug?: {
|
|
42
|
+
directActiveCaloriesKcal: number;
|
|
43
|
+
totalCaloriesKcal: number;
|
|
44
|
+
basalCaloriesKcal: number;
|
|
45
|
+
mergedActiveCaloriesKcal: number;
|
|
46
|
+
fallbackContributionKcal: number;
|
|
47
|
+
fallbackUsedSegments: number;
|
|
48
|
+
fallbackSuppressedSegments: number;
|
|
49
|
+
idleGateSuppressedSegments: number;
|
|
50
|
+
basalSeedApplied: boolean;
|
|
51
|
+
seededFromBeforeSince: boolean;
|
|
52
|
+
basalSeedAgeHours?: number;
|
|
53
|
+
basalIntervalsCount: number;
|
|
54
|
+
};
|
|
55
|
+
};
|
|
56
|
+
export type GetExerciseDataOptions = {
|
|
57
|
+
sinceMillis: number;
|
|
58
|
+
untilMillis: number;
|
|
59
|
+
type?: 'steps' | 'activetimes' | 'calories' | 'distances' | 'all';
|
|
60
|
+
bucketPeriod?: 'daily' | 'hourly';
|
|
61
|
+
debug?: boolean;
|
|
62
|
+
};
|
|
63
|
+
export declare function getExerciseData(options: GetExerciseDataOptions): Promise<ExerciseData>;
|
|
64
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/exercise/index.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,UAAU,GAAG,SAAS,GAAG,gBAAgB,CAAC;AAEtD,MAAM,MAAM,iBAAiB,GAAG;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,UAAU,CAAC;CACxB,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAAG;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,cAAc,EAAE,MAAM,CAAC;IACvB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,IAAI,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,EAAE,cAAc,CAAC;IAC/B,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,iBAAiB,CAAC;CACzB,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,qBAAqB,EAAE,MAAM,CAAC;IAE9B,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,mBAAmB,EAAE,MAAM,CAAC;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,qBAAqB,EAAE,CAAC;IACjC,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,SAAS,CAAC,EAAE;QACV,mBAAmB,EAAE,OAAO,CAAC;QAC7B,aAAa,EAAE,MAAM,CAAC;KACvB,CAAC;IACF,WAAW,CAAC,EAAE;QACZ,oBAAoB,EAAE,MAAM,CAAC;QAC7B,0BAA0B,EAAE,MAAM,CAAC;QACnC,0BAA0B,EAAE,MAAM,CAAC;QACnC,gBAAgB,EAAE,OAAO,CAAC;QAC1B,qBAAqB,EAAE,OAAO,CAAC;QAC/B,iBAAiB,CAAC,EAAE,MAAM,CAAC;KAC5B,CAAC;IACF,KAAK,CAAC,EAAE;QACN,wBAAwB,EAAE,MAAM,CAAC;QACjC,iBAAiB,EAAE,MAAM,CAAC;QAC1B,iBAAiB,EAAE,MAAM,CAAC;QAC1B,wBAAwB,EAAE,MAAM,CAAC;QACjC,wBAAwB,EAAE,MAAM,CAAC;QACjC,oBAAoB,EAAE,MAAM,CAAC;QAC7B,0BAA0B,EAAE,MAAM,CAAC;QACnC,0BAA0B,EAAE,MAAM,CAAC;QACnC,gBAAgB,EAAE,OAAO,CAAC;QAC1B,qBAAqB,EAAE,OAAO,CAAC;QAC/B,iBAAiB,CAAC,EAAE,MAAM,CAAC;QAC3B,mBAAmB,EAAE,MAAM,CAAC;KAC7B,CAAC;CACH,CAAC;AAEF,MAAM,MAAM,sBAAsB,GAAG;IACnC,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,CAAC,EAAE,OAAO,GAAG,aAAa,GAAG,UAAU,GAAG,WAAW,GAAG,KAAK,CAAC;IAClE,YAAY,CAAC,EAAE,OAAO,GAAG,QAAQ,CAAC;IAClC,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB,CAAC;AAEF,wBAAgB,eAAe,CAC7B,OAAO,EAAE,sBAAsB,GAC9B,OAAO,CAAC,YAAY,CAAC,CAEvB"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { connect, type ConnectOptions, type ConnectResult, type PermissionScope, } from './connect';
|
|
2
|
+
export { getExerciseData, type ExerciseData, type GetExerciseDataOptions, type TrustLevel, type UnifiedIntervalRecord, type UnifiedRecordMeta, } from './exercise';
|
|
3
|
+
export { getAllowDatasource, setAllowDatasource, type DatasourceConfig, type DatasourceType, } from './datasource';
|
|
4
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.tsx"],"names":[],"mappings":"AAAA,OAAO,EACL,OAAO,EACP,KAAK,cAAc,EACnB,KAAK,aAAa,EAClB,KAAK,eAAe,GACrB,MAAM,WAAW,CAAC;AAEnB,OAAO,EACL,eAAe,EACf,KAAK,YAAY,EACjB,KAAK,sBAAsB,EAC3B,KAAK,UAAU,EACf,KAAK,qBAAqB,EAC1B,KAAK,iBAAiB,GACvB,MAAM,YAAY,CAAC;AAEpB,OAAO,EACL,kBAAkB,EAClB,kBAAkB,EAClB,KAAK,gBAAgB,EACrB,KAAK,cAAc,GACpB,MAAM,cAAc,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { ConnectResult } from '../connect';
|
|
2
|
+
import type { DatasourceConfig } from '../datasource';
|
|
3
|
+
import type { ExerciseData } from '../exercise';
|
|
4
|
+
type HealthConnectModuleSpec = {
|
|
5
|
+
connect(optionsJson: string): Promise<ConnectResult>;
|
|
6
|
+
getExerciseData(optionsJson: string): Promise<ExerciseData>;
|
|
7
|
+
setAllowDatasource(configJson: string): Promise<{
|
|
8
|
+
success: boolean;
|
|
9
|
+
}>;
|
|
10
|
+
getAllowDatasource(): Promise<DatasourceConfig>;
|
|
11
|
+
};
|
|
12
|
+
export declare function getHealthConnectModule(): HealthConnectModuleSpec;
|
|
13
|
+
export {};
|
|
14
|
+
//# sourceMappingURL=module.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"module.d.ts","sourceRoot":"","sources":["../../../../src/native/module.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAChD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AACtD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEhD,KAAK,uBAAuB,GAAG;IAC7B,OAAO,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC;IACrD,eAAe,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC;IAC5D,kBAAkB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,OAAO,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;IACtE,kBAAkB,IAAI,OAAO,CAAC,gBAAgB,CAAC,CAAC;CACjD,CAAC;AAMF,wBAAgB,sBAAsB,IAAI,uBAAuB,CAKhE"}
|