@react-native-kakao/map 2.2.7 → 3.2.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.
Files changed (165) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/README.md +5 -38
  3. package/RNCKakaoMap.podspec +16 -52
  4. package/android/build.gradle +12 -107
  5. package/android/consumer-rules.pro +2 -0
  6. package/android/src/main/AndroidManifest.xml +3 -1
  7. package/android/src/main/java/net/mjstudio/rnkakao/map/CameraOptions.kt +61 -0
  8. package/android/src/main/java/net/mjstudio/rnkakao/map/KakaoMapModule.kt +131 -0
  9. package/android/src/main/java/net/mjstudio/rnkakao/map/KakaoMapView.kt +1257 -0
  10. package/android/src/main/java/net/mjstudio/rnkakao/map/MapCameraCommands.kt +306 -0
  11. package/android/src/main/java/net/mjstudio/rnkakao/map/MapDimScreen.kt +431 -0
  12. package/android/src/main/java/net/mjstudio/rnkakao/map/MapGui.kt +899 -0
  13. package/android/src/main/java/net/mjstudio/rnkakao/map/MapImageLoader.kt +232 -0
  14. package/android/src/main/java/net/mjstudio/rnkakao/map/MapLabels.kt +1572 -0
  15. package/android/src/main/java/net/mjstudio/rnkakao/map/MapOptions.kt +360 -0
  16. package/android/src/main/java/net/mjstudio/rnkakao/map/MapRoutes.kt +805 -0
  17. package/android/src/main/java/net/mjstudio/rnkakao/map/MapShapes.kt +1096 -0
  18. package/android/src/main/java/net/mjstudio/rnkakao/map/MapUtilities.kt +495 -0
  19. package/expo-module.config.json +5 -0
  20. package/ios/CameraOptions.swift +41 -0
  21. package/ios/KakaoMapModule.swift +204 -0
  22. package/ios/KakaoMapView.swift +1018 -0
  23. package/ios/MapCameraCommands.swift +451 -0
  24. package/ios/MapDimScreen.swift +463 -0
  25. package/ios/MapGui.swift +1099 -0
  26. package/ios/MapImageLoader.swift +177 -0
  27. package/ios/MapLabels.swift +1676 -0
  28. package/ios/MapOptions.swift +374 -0
  29. package/ios/MapRoutes.swift +903 -0
  30. package/ios/MapShapes.swift +1294 -0
  31. package/ios/MapUnits.swift +20 -0
  32. package/ios/MapUtilities.swift +365 -0
  33. package/lib/commonjs/index.js +180 -29
  34. package/lib/commonjs/index.js.map +1 -1
  35. package/lib/commonjs/index.web.js +18 -0
  36. package/lib/commonjs/index.web.js.map +1 -0
  37. package/lib/commonjs/package.json +1 -0
  38. package/lib/commonjs/types-assets.js +2 -0
  39. package/lib/commonjs/types-assets.js.map +1 -0
  40. package/lib/commonjs/types-camera.js +6 -0
  41. package/lib/commonjs/types-camera.js.map +1 -0
  42. package/lib/commonjs/types-dim.js +6 -0
  43. package/lib/commonjs/types-dim.js.map +1 -0
  44. package/lib/commonjs/types-gui.js +6 -0
  45. package/lib/commonjs/types-gui.js.map +1 -0
  46. package/lib/commonjs/types-labels.js +6 -0
  47. package/lib/commonjs/types-labels.js.map +1 -0
  48. package/lib/commonjs/types-routes.js +6 -0
  49. package/lib/commonjs/types-routes.js.map +1 -0
  50. package/lib/commonjs/types-shapes.js +6 -0
  51. package/lib/commonjs/types-shapes.js.map +1 -0
  52. package/lib/commonjs/types-utilities.js +6 -0
  53. package/lib/commonjs/types-utilities.js.map +1 -0
  54. package/lib/commonjs/types.js +6 -0
  55. package/lib/commonjs/types.js.map +1 -0
  56. package/lib/module/index.js +180 -22
  57. package/lib/module/index.js.map +1 -1
  58. package/lib/module/index.web.js +13 -0
  59. package/lib/module/index.web.js.map +1 -0
  60. package/lib/module/types-assets.js +2 -0
  61. package/lib/module/types-assets.js.map +1 -0
  62. package/lib/module/types-camera.js +4 -0
  63. package/lib/module/types-camera.js.map +1 -0
  64. package/lib/module/types-dim.js +4 -0
  65. package/lib/module/types-dim.js.map +1 -0
  66. package/lib/module/types-gui.js +4 -0
  67. package/lib/module/types-gui.js.map +1 -0
  68. package/lib/module/types-labels.js +4 -0
  69. package/lib/module/types-labels.js.map +1 -0
  70. package/lib/module/types-routes.js +4 -0
  71. package/lib/module/types-routes.js.map +1 -0
  72. package/lib/module/types-shapes.js +4 -0
  73. package/lib/module/types-shapes.js.map +1 -0
  74. package/lib/module/types-utilities.js +4 -0
  75. package/lib/module/types-utilities.js.map +1 -0
  76. package/lib/module/types.js +4 -0
  77. package/lib/module/types.js.map +1 -0
  78. package/lib/typescript/src/index.d.ts +59 -9
  79. package/lib/typescript/src/index.d.ts.map +1 -1
  80. package/lib/typescript/src/types-assets.d.ts +9 -0
  81. package/lib/typescript/src/types-assets.d.ts.map +1 -0
  82. package/lib/typescript/src/types-camera.d.ts +123 -0
  83. package/lib/typescript/src/types-camera.d.ts.map +1 -0
  84. package/lib/typescript/src/types-dim.d.ts +54 -0
  85. package/lib/typescript/src/types-dim.d.ts.map +1 -0
  86. package/lib/typescript/src/types-gui.d.ts +189 -0
  87. package/lib/typescript/src/types-gui.d.ts.map +1 -0
  88. package/lib/typescript/src/types-labels.d.ts +246 -0
  89. package/lib/typescript/src/types-labels.d.ts.map +1 -0
  90. package/lib/typescript/src/types-routes.d.ts +162 -0
  91. package/lib/typescript/src/types-routes.d.ts.map +1 -0
  92. package/lib/typescript/src/types-shapes.d.ts +253 -0
  93. package/lib/typescript/src/types-shapes.d.ts.map +1 -0
  94. package/lib/typescript/src/types-utilities.d.ts +130 -0
  95. package/lib/typescript/src/types-utilities.d.ts.map +1 -0
  96. package/lib/typescript/src/types.d.ts +222 -0
  97. package/lib/typescript/src/types.d.ts.map +1 -0
  98. package/package.json +24 -77
  99. package/src/index.tsx +322 -0
  100. package/src/index.web.tsx +46 -0
  101. package/src/types-assets.ts +8 -0
  102. package/src/types-camera.ts +97 -0
  103. package/src/types-dim.ts +54 -0
  104. package/src/types-gui.ts +171 -0
  105. package/src/types-labels.ts +207 -0
  106. package/src/types-routes.ts +118 -0
  107. package/src/types-shapes.ts +244 -0
  108. package/src/types-utilities.ts +115 -0
  109. package/src/types.ts +223 -0
  110. package/android/gradle.properties +0 -1
  111. package/android/src/main/AndroidManifestNew.xml +0 -2
  112. package/android/src/main/java/net/mjstudio/rnkakao/map/RNCKakaoMapModule.kt +0 -25
  113. package/android/src/main/java/net/mjstudio/rnkakao/map/RNCKakaoMapPackage.kt +0 -35
  114. package/android/src/newarch/KakaoMapSpec.kt +0 -7
  115. package/android/src/oldarch/KakaoMapSpec.kt +0 -14
  116. package/ios/Component/Map/RNCKakaoMapView.h +0 -23
  117. package/ios/Component/Map/RNCKakaoMapView.mm +0 -209
  118. package/ios/Component/Map/RNCKakaoMapViewManager.mm +0 -19
  119. package/ios/NativeModule/RNCKakaoMap.h +0 -7
  120. package/ios/NativeModule/RNCKakaoMap.mm +0 -19
  121. package/ios/NativeModule/RNCKakaoMapManager.swift +0 -7
  122. package/ios/Util/Util.h +0 -97
  123. package/lib/commonjs/component/KakaoMapView.js +0 -54
  124. package/lib/commonjs/component/KakaoMapView.js.map +0 -1
  125. package/lib/commonjs/spec/NativeKakaoMap.js +0 -9
  126. package/lib/commonjs/spec/NativeKakaoMap.js.map +0 -1
  127. package/lib/commonjs/spec/RNCKakaoMapViewNativeComponent.js +0 -15
  128. package/lib/commonjs/spec/RNCKakaoMapViewNativeComponent.js.map +0 -1
  129. package/lib/commonjs/type/type.js +0 -2
  130. package/lib/commonjs/type/type.js.map +0 -1
  131. package/lib/commonjs/util/Const.js +0 -14
  132. package/lib/commonjs/util/Const.js.map +0 -1
  133. package/lib/commonjs/util/Util.js +0 -15
  134. package/lib/commonjs/util/Util.js.map +0 -1
  135. package/lib/module/component/KakaoMapView.js +0 -48
  136. package/lib/module/component/KakaoMapView.js.map +0 -1
  137. package/lib/module/spec/NativeKakaoMap.js +0 -3
  138. package/lib/module/spec/NativeKakaoMap.js.map +0 -1
  139. package/lib/module/spec/RNCKakaoMapViewNativeComponent.js +0 -10
  140. package/lib/module/spec/RNCKakaoMapViewNativeComponent.js.map +0 -1
  141. package/lib/module/type/type.js +0 -2
  142. package/lib/module/type/type.js.map +0 -1
  143. package/lib/module/util/Const.js +0 -8
  144. package/lib/module/util/Const.js.map +0 -1
  145. package/lib/module/util/Util.js +0 -9
  146. package/lib/module/util/Util.js.map +0 -1
  147. package/lib/typescript/src/component/KakaoMapView.d.ts +0 -25
  148. package/lib/typescript/src/component/KakaoMapView.d.ts.map +0 -1
  149. package/lib/typescript/src/spec/NativeKakaoMap.d.ts +0 -7
  150. package/lib/typescript/src/spec/NativeKakaoMap.d.ts.map +0 -1
  151. package/lib/typescript/src/spec/RNCKakaoMapViewNativeComponent.d.ts +0 -32
  152. package/lib/typescript/src/spec/RNCKakaoMapViewNativeComponent.d.ts.map +0 -1
  153. package/lib/typescript/src/type/type.d.ts +0 -8
  154. package/lib/typescript/src/type/type.d.ts.map +0 -1
  155. package/lib/typescript/src/util/Const.d.ts +0 -8
  156. package/lib/typescript/src/util/Const.d.ts.map +0 -1
  157. package/lib/typescript/src/util/Util.d.ts +0 -3
  158. package/lib/typescript/src/util/Util.d.ts.map +0 -1
  159. package/src/component/KakaoMapView.tsx +0 -82
  160. package/src/index.ts +0 -43
  161. package/src/spec/NativeKakaoMap.ts +0 -8
  162. package/src/spec/RNCKakaoMapViewNativeComponent.ts +0 -38
  163. package/src/type/type.ts +0 -7
  164. package/src/util/Const.ts +0 -7
  165. package/src/util/Util.ts +0 -11
@@ -0,0 +1,306 @@
1
+ package net.mjstudio.rnkakao.map
2
+
3
+ import android.os.Looper
4
+ import com.kakao.vectormap.GestureType
5
+ import com.kakao.vectormap.KakaoMap
6
+ import com.kakao.vectormap.LatLng
7
+ import com.kakao.vectormap.camera.CameraAnimation
8
+ import com.kakao.vectormap.camera.CameraPosition
9
+ import com.kakao.vectormap.camera.CameraUpdate
10
+ import com.kakao.vectormap.camera.CameraUpdateFactory
11
+ import expo.modules.kotlin.exception.CodedException
12
+ import kotlin.math.roundToInt
13
+
14
+ private const val CAMERA_UNSUPPORTED = "ERR_KAKAO_MAP_UNSUPPORTED_PLATFORM_FEATURE"
15
+ private const val CAMERA_DESTROYED = "ERR_KAKAO_MAP_VIEW_DESTROYED"
16
+
17
+ internal class MapCameraCommands(
18
+ map: KakaoMap,
19
+ private val density: Float,
20
+ ) {
21
+ private var map: KakaoMap? = map
22
+
23
+ fun command(command: Map<String, Any?>): Map<String, Any?> {
24
+ requireMainThread()
25
+ val type = command.string("type")
26
+ return when (type) {
27
+ "getCapabilities" -> {
28
+ capabilities()
29
+ }
30
+
31
+ "getState" -> {
32
+ throw CodedException(
33
+ "ERR_KAKAO_MAP_CAMERA_ASYNC_REQUIRED",
34
+ "Android camera state must be read through KakaoMap.getCameraPosition(listener).",
35
+ null,
36
+ )
37
+ }
38
+
39
+ "moveToHeight" -> {
40
+ moveToHeight(command)
41
+ }
42
+
43
+ "transform" -> {
44
+ unsupported("Camera transforms are not exposed by Kakao Maps Android SDK 2.15.2.")
45
+ }
46
+
47
+ "fit" -> {
48
+ fit(command)
49
+ }
50
+
51
+ "canShow" -> {
52
+ canShow(command)
53
+ }
54
+
55
+ "heightAtLevel" -> {
56
+ unsupported("Height conversion by zoom level is not exposed by Kakao Maps Android SDK 2.15.2.")
57
+ }
58
+
59
+ "resetOrientation" -> {
60
+ unsupported("Orientation reset is not exposed by Kakao Maps Android SDK 2.15.2.")
61
+ }
62
+
63
+ "setBackToNorthDuringAutoElevation" -> {
64
+ unsupported(
65
+ "Back-to-north during auto elevation is not exposed by Kakao Maps Android SDK 2.15.2.",
66
+ )
67
+ }
68
+
69
+ "setReferencePointLock" -> {
70
+ setReferencePointLock(command)
71
+ }
72
+
73
+ else -> {
74
+ invalid("Unsupported advanced camera command: $type.")
75
+ }
76
+ }
77
+ }
78
+
79
+ fun dispose() {
80
+ requireMainThread()
81
+ map = null
82
+ }
83
+
84
+ fun stateResult(position: CameraPosition): Map<String, Any?> {
85
+ requireMainThread()
86
+ requireMap()
87
+ return mapOf("type" to "state", "camera" to position.advancedPayload())
88
+ }
89
+
90
+ private fun moveToHeight(command: Map<String, Any?>): Map<String, Any?> {
91
+ rejectOperationId(command)
92
+ val camera = command.objectValue("camera")
93
+ val latitude = camera.finiteNumber("latitude")
94
+ val longitude = camera.finiteNumber("longitude")
95
+ val height = camera.finiteNumber("height")
96
+ val tilt = camera.finiteNumber("tilt")
97
+ val rotation = camera.finiteNumber("rotation")
98
+ requireCoordinate(latitude, longitude)
99
+ if (height <= 0.0 || tilt !in 0.0..90.0) {
100
+ invalid("Camera height must be positive and tilt must be between 0 and 90 degrees.")
101
+ }
102
+ val position =
103
+ CameraPosition.from(
104
+ latitude,
105
+ longitude,
106
+ -1,
107
+ Math.toRadians(tilt),
108
+ rotation.toNativeCameraRotation(),
109
+ height,
110
+ )
111
+ dispatch(CameraUpdateFactory.newCameraPosition(position), command.animation())
112
+ return dispatched()
113
+ }
114
+
115
+ private fun fit(command: Map<String, Any?>): Map<String, Any?> {
116
+ rejectOperationId(command)
117
+ val points = command.coordinates("coordinates")
118
+ val paddingDp = command.optionalFiniteNumber("padding") ?: 0.0
119
+ if (paddingDp < 0.0) invalid("Camera fit padding must be non-negative.")
120
+ val paddingPx = checkedPixel(paddingDp)
121
+ val levelLimit = command.optionalInteger("levelLimit")
122
+ val map = requireMap()
123
+ if (levelLimit != null && levelLimit !in map.cameraMinLevel..map.cameraMaxLevel) {
124
+ invalid("Camera fit levelLimit is outside the configured camera range.")
125
+ }
126
+ val update =
127
+ if (levelLimit == null) {
128
+ CameraUpdateFactory.fitMapPoints(points, paddingPx)
129
+ } else {
130
+ CameraUpdateFactory.fitMapPoints(points, paddingPx, levelLimit)
131
+ }
132
+ dispatch(update, command.animation())
133
+ return dispatched()
134
+ }
135
+
136
+ private fun canShow(command: Map<String, Any?>): Map<String, Any?> {
137
+ val rotation = command.optionalFiniteNumber("rotation") ?: 0.0
138
+ val weight = command.optionalFiniteNumber("weight") ?: 1.0
139
+ if (rotation != 0.0 || weight != 1.0 || command["rect"] != null) {
140
+ unsupported("Android canShow supports only coordinates and zoomLevel.")
141
+ }
142
+ val map = requireMap()
143
+ val zoomLevel = command.integer("zoomLevel")
144
+ if (zoomLevel !in map.cameraMinLevel..map.cameraMaxLevel) {
145
+ invalid("canShow zoomLevel is outside the configured camera range.")
146
+ }
147
+ return mapOf("type" to "canShow", "value" to map.canShowMapPoints(zoomLevel, *command.coordinates("coordinates")))
148
+ }
149
+
150
+ private fun setReferencePointLock(command: Map<String, Any?>): Map<String, Any?> {
151
+ val gesture = referenceGesture(command.string("gesture"))
152
+ requireMap().setFixedCenter(command.boolean("locked"), gesture)
153
+ return mapOf("type" to "applied")
154
+ }
155
+
156
+ private fun dispatch(
157
+ update: CameraUpdate,
158
+ animation: CameraAnimation?,
159
+ ) {
160
+ val map = requireMap()
161
+ if (animation == null) map.moveCamera(update) else map.moveCamera(update, animation)
162
+ }
163
+
164
+ private fun Map<String, Any?>.animation(): CameraAnimation? {
165
+ if (!containsKey("animation")) return null
166
+ val raw = this["animation"]
167
+ val animation = raw as? Map<*, *> ?: invalid("animation must be an object.")
168
+ val normalized = animation.stringKeys("animation")
169
+ val duration = normalized.integer("duration")
170
+ if (duration < 0) invalid("Animation duration must be non-negative.")
171
+ val autoElevation = normalized.optionalBoolean("autoElevation") ?: false
172
+ val consecutive = normalized.optionalBoolean("consecutive") ?: false
173
+ if (duration == 0) return null
174
+ return CameraAnimation.from(
175
+ duration,
176
+ autoElevation,
177
+ consecutive,
178
+ )
179
+ }
180
+
181
+ private fun capabilities() =
182
+ mapOf(
183
+ "type" to "capabilities",
184
+ "capabilities" to
185
+ mapOf(
186
+ "transform" to false,
187
+ "absoluteHeight" to true,
188
+ "heightAtLevel" to false,
189
+ "fit" to mapOf("padding" to true, "levelLimit" to true),
190
+ "canShow" to mapOf("basic" to true, "rotation" to false, "weight" to false, "rect" to false),
191
+ "resetOrientation" to false,
192
+ "backToNorthDuringAutoElevation" to false,
193
+ "animation" to mapOf("autoElevation" to true, "consecutive" to true, "operationCompletion" to false),
194
+ "referencePoint" to mapOf("supported" to true, "gestures" to REFERENCE_GESTURES),
195
+ ),
196
+ )
197
+
198
+ private fun requireMap() = map ?: throw CodedException(CAMERA_DESTROYED, "The map view was destroyed.", null)
199
+
200
+ private fun rejectOperationId(command: Map<String, Any?>) {
201
+ if (command["operationId"] != null) {
202
+ unsupported("Android SDK 2.15.2 does not expose per-camera-operation completion callbacks.")
203
+ }
204
+ }
205
+
206
+ private fun checkedPixel(value: Double): Int {
207
+ val pixels = value * density
208
+ if (!pixels.isFinite() || pixels > Int.MAX_VALUE) invalid("Camera fit padding exceeds the supported pixel range.")
209
+ return pixels.roundToInt()
210
+ }
211
+ }
212
+
213
+ private val REFERENCE_GESTURES = listOf("rotate", "zoom", "tilt", "rotateZoom", "oneFingerZoom")
214
+
215
+ private fun referenceGesture(value: String) =
216
+ when (value) {
217
+ "rotate" -> GestureType.Rotate
218
+ "zoom" -> GestureType.Zoom
219
+ "tilt" -> GestureType.Tilt
220
+ "rotateZoom" -> GestureType.RotateZoom
221
+ "oneFingerZoom" -> GestureType.OneFingerZoom
222
+ else -> invalid("Unsupported reference-point gesture: $value.")
223
+ }
224
+
225
+ private fun CameraPosition.advancedPayload() =
226
+ mapOf(
227
+ "latitude" to position.latitude,
228
+ "longitude" to position.longitude,
229
+ "zoomLevel" to zoomLevel,
230
+ "height" to height,
231
+ "tilt" to Math.toDegrees(tiltAngle),
232
+ "rotation" to Math.toDegrees(rotationAngle).normalizedCameraDegrees(),
233
+ )
234
+
235
+ private fun dispatched() = mapOf("type" to "dispatched")
236
+
237
+ private fun Map<String, Any?>.coordinates(name: String): Array<LatLng> {
238
+ val values = this[name] as? List<*> ?: invalid("$name must be an array.")
239
+ if (values.isEmpty()) invalid("$name must contain at least one coordinate.")
240
+ return values
241
+ .mapIndexed { index, value ->
242
+ val coordinate =
243
+ (value as? Map<*, *>)?.stringKeys("$name[$index]")
244
+ ?: invalid("$name[$index] must be an object.")
245
+ val latitude = coordinate.finiteNumber("latitude")
246
+ val longitude = coordinate.finiteNumber("longitude")
247
+ requireCoordinate(latitude, longitude)
248
+ LatLng.from(latitude, longitude)
249
+ }.toTypedArray()
250
+ }
251
+
252
+ private fun Map<String, Any?>.objectValue(name: String): Map<String, Any?> =
253
+ (this[name] as? Map<*, *>)?.stringKeys(name) ?: invalid("$name must be an object.")
254
+
255
+ private fun Map<*, *>.stringKeys(path: String): Map<String, Any?> =
256
+ entries.associate { (key, value) ->
257
+ val stringKey = key as? String ?: invalid("$path must use string object keys.")
258
+ stringKey to value
259
+ }
260
+
261
+ private fun Map<String, Any?>.string(name: String) = this[name] as? String ?: invalid("$name must be a string.")
262
+
263
+ private fun Map<String, Any?>.boolean(name: String) = this[name] as? Boolean ?: invalid("$name must be a boolean.")
264
+
265
+ private fun Map<String, Any?>.optionalBoolean(name: String): Boolean? {
266
+ if (!containsKey(name)) return null
267
+ val value = this[name]
268
+ return value as? Boolean ?: invalid("$name must be a boolean.")
269
+ }
270
+
271
+ private fun Map<String, Any?>.finiteNumber(name: String): Double = optionalFiniteNumber(name) ?: invalid("$name must be a finite number.")
272
+
273
+ private fun Map<String, Any?>.optionalFiniteNumber(name: String): Double? {
274
+ if (!containsKey(name)) return null
275
+ val value = this[name]
276
+ val number = (value as? Number)?.toDouble() ?: invalid("$name must be a finite number.")
277
+ if (!number.isFinite()) invalid("$name must be a finite number.")
278
+ return number
279
+ }
280
+
281
+ private fun Map<String, Any?>.integer(name: String): Int = optionalInteger(name) ?: invalid("$name must be an integer.")
282
+
283
+ private fun Map<String, Any?>.optionalInteger(name: String): Int? {
284
+ val number = optionalFiniteNumber(name) ?: return null
285
+ if (number % 1.0 != 0.0 || number < Int.MIN_VALUE || number > Int.MAX_VALUE) invalid("$name must be an integer.")
286
+ return number.toInt()
287
+ }
288
+
289
+ private fun requireCoordinate(
290
+ latitude: Double,
291
+ longitude: Double,
292
+ ) {
293
+ if (latitude !in -90.0..90.0 || longitude !in -180.0..180.0) {
294
+ invalid("Coordinate latitude or longitude is outside its valid range.")
295
+ }
296
+ }
297
+
298
+ private fun requireMainThread() {
299
+ if (Looper.myLooper() != Looper.getMainLooper()) {
300
+ throw CodedException("ERR_KAKAO_MAP_WRONG_THREAD", "Advanced camera commands must run on the main thread.", null)
301
+ }
302
+ }
303
+
304
+ private fun invalid(message: String): Nothing = throw CodedException(INVALID_ARGUMENT, message, null)
305
+
306
+ private fun unsupported(message: String): Nothing = throw CodedException(CAMERA_UNSUPPORTED, message, null)