craft-native 0.0.90 → 0.0.92

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.
Files changed (31) hide show
  1. package/dist/android/src/index.d.ts +36 -1
  2. package/dist/android/src/index.js +332 -44
  3. package/dist/android/src/promise-runtime.d.ts +3 -0
  4. package/dist/android/templates/CraftBridge.kt.template +2167 -1177
  5. package/dist/android/templates/CraftHealthConnect.kt.template +45 -6
  6. package/dist/android/templates/CraftHealthConnectStub.kt.template +7 -1
  7. package/dist/android/templates/CraftNative.kt.template +2250 -0
  8. package/dist/android/templates/LocationRecordingService.kt.template +34 -9
  9. package/dist/android/templates/MainActivity.kt.template +42 -8
  10. package/dist/android/templates/proguard-rules.pro.template +4 -1
  11. package/dist/android/templates/test-bridges.html +10 -33
  12. package/dist/api/index.d.ts +1 -1
  13. package/dist/api/ios-advanced.d.ts +8 -5
  14. package/dist/api/live-activity-handle.d.ts +6 -0
  15. package/dist/api/mobile.d.ts +25 -7
  16. package/dist/api/window.d.ts +2 -0
  17. package/dist/cli.js +452 -129
  18. package/dist/index.cjs +77 -22
  19. package/dist/index.js +77 -22
  20. package/dist/ios/src/index.d.ts +1 -1
  21. package/dist/ios/src/index.js +23 -5
  22. package/dist/ios/templates/CraftApp.swift +706 -92
  23. package/dist/ios/templates/CraftWatchApp.swift.template +8 -0
  24. package/dist/ios/templates/WatchApp.Info.plist.template +2 -0
  25. package/dist/ios/templates/WatchExtension.Info.plist.template +34 -0
  26. package/dist/ios/templates/project.yml.template +10 -4
  27. package/dist/mobile.js +52 -21
  28. package/dist/scaffold-version.d.ts +5 -0
  29. package/package.json +1 -1
  30. package/dist/android/templates/CraftBridgeExtensions.kt.template +0 -383
  31. package/dist/android/templates/CraftWidgetProvider.kt.template +0 -246
@@ -35,7 +35,10 @@ class CraftHealthConnect(
35
35
  }
36
36
 
37
37
  private val scope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
38
+ private val closed = java.util.concurrent.atomic.AtomicBoolean(false)
38
39
  private val permissionContract = PermissionController.createRequestPermissionResultContract()
40
+ private val authorizationPending = java.util.concurrent.atomic.AtomicBoolean(false)
41
+ @Volatile
39
42
  private var pendingPermissions: Set<String> = emptySet()
40
43
 
41
44
  private val client: HealthConnectClient?
@@ -46,7 +49,13 @@ class CraftHealthConnect(
46
49
  }
47
50
 
48
51
  fun requestAuthorization(typesJson: String) {
49
- val requested = permissionsFor(JSONArray(typesJson))
52
+ val requested = try {
53
+ permissionsFor(JSONArray(typesJson)).also {
54
+ require(it.isNotEmpty()) { "At least one Health Connect permission type is required" }
55
+ }
56
+ } catch (error: Exception) {
57
+ return reject("_craftFitnessAuthReject", error.message ?: "Health permissions are invalid")
58
+ }
50
59
  val healthClient = client ?: return reject("_craftFitnessAuthReject", "Health Connect is unavailable or requires an update")
51
60
  scope.launch {
52
61
  try {
@@ -55,9 +64,25 @@ class CraftHealthConnect(
55
64
  resolve("_craftFitnessAuthResolve", "true")
56
65
  } else {
57
66
  pendingPermissions = requested
67
+ authorizationPending.set(true)
68
+ if (closed.get()) {
69
+ authorizationPending.set(false)
70
+ pendingPermissions = emptySet()
71
+ return@launch
72
+ }
58
73
  activity.runOnUiThread {
59
- val intent = permissionContract.createIntent(activity, requested)
60
- activity.startActivityForResult(intent, PERMISSION_REQUEST_CODE)
74
+ if (closed.get()) return@runOnUiThread
75
+ try {
76
+ val intent = permissionContract.createIntent(activity, requested)
77
+ activity.startActivityForResult(intent, PERMISSION_REQUEST_CODE)
78
+ } catch (error: Exception) {
79
+ authorizationPending.set(false)
80
+ pendingPermissions = emptySet()
81
+ reject(
82
+ "_craftFitnessAuthReject",
83
+ error.message ?: "Health permissions could not be requested"
84
+ )
85
+ }
61
86
  }
62
87
  }
63
88
  } catch (error: Exception) {
@@ -68,9 +93,17 @@ class CraftHealthConnect(
68
93
 
69
94
  fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?): Boolean {
70
95
  if (requestCode != PERMISSION_REQUEST_CODE) return false
71
- val granted = permissionContract.parseResult(resultCode, data)
72
- resolve("_craftFitnessAuthResolve", if (granted.containsAll(pendingPermissions)) "true" else "false")
96
+ if (closed.get()) return true
97
+ if (!authorizationPending.compareAndSet(true, false)) return true
98
+ val requested = pendingPermissions
73
99
  pendingPermissions = emptySet()
100
+ val granted = try {
101
+ permissionContract.parseResult(resultCode, data)
102
+ } catch (error: Exception) {
103
+ reject("_craftFitnessAuthReject", error.message ?: "Health permission result could not be read")
104
+ return true
105
+ }
106
+ resolve("_craftFitnessAuthResolve", if (granted.containsAll(requested)) "true" else "false")
74
107
  return true
75
108
  }
76
109
 
@@ -135,7 +168,7 @@ class CraftHealthConnect(
135
168
  endTime = end,
136
169
  endZoneOffset = zone,
137
170
  exerciseType = exerciseType,
138
- title = "WildLoop",
171
+ title = activity.applicationInfo.loadLabel(activity.packageManager).toString(),
139
172
  metadata = Metadata.manualEntry(clientRecordId = activityId)
140
173
  ))
141
174
  val distance = data.optDouble("distanceMeters", 0.0)
@@ -166,6 +199,9 @@ class CraftHealthConnect(
166
199
  }
167
200
 
168
201
  fun close() {
202
+ closed.set(true)
203
+ authorizationPending.set(false)
204
+ pendingPermissions = emptySet()
169
205
  scope.cancel()
170
206
  }
171
207
 
@@ -183,13 +219,16 @@ class CraftHealthConnect(
183
219
  permissions.add(HealthPermission.getWritePermission(ActiveCaloriesBurnedRecord::class))
184
220
  permissions.add(HealthPermission.getWritePermission(DistanceRecord::class))
185
221
  }
222
+ else -> throw IllegalArgumentException("Unsupported Health Connect permission type: ${types.optString(index)}")
186
223
  }
187
224
  }
188
225
  return permissions
189
226
  }
190
227
 
191
228
  private fun resolve(callback: String, json: String) {
229
+ if (closed.get()) return
192
230
  activity.runOnUiThread {
231
+ if (closed.get()) return@runOnUiThread
193
232
  webView.evaluateJavascript("window.$callback && window.$callback($json)", null)
194
233
  }
195
234
  }
@@ -9,6 +9,8 @@ class CraftHealthConnect(
9
9
  private val activity: Activity,
10
10
  private val webView: WebView
11
11
  ) {
12
+ private val closed = java.util.concurrent.atomic.AtomicBoolean(false)
13
+
12
14
  fun requestAuthorization(typesJson: String) = reject("_craftFitnessAuthReject")
13
15
 
14
16
  fun getData(type: String, startDate: Long, endDate: Long) = reject("_craftFitnessDataReject")
@@ -17,10 +19,14 @@ class CraftHealthConnect(
17
19
 
18
20
  fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?): Boolean = false
19
21
 
20
- fun close() = Unit
22
+ fun close() {
23
+ closed.set(true)
24
+ }
21
25
 
22
26
  private fun reject(callback: String) {
27
+ if (closed.get()) return
23
28
  activity.runOnUiThread {
29
+ if (closed.get()) return@runOnUiThread
24
30
  val reason = JSONObject.quote("Health Connect is disabled")
25
31
  webView.evaluateJavascript("window.$callback && window.$callback($reason)", null)
26
32
  }