Skip to content

Commit 577ed57

Browse files
authored
[Merge PR] Feature: custom style (#308, with @fogbar)
2 parents 0ec4d0b + 5d18d31 commit 577ed57

38 files changed

+481
-185
lines changed

android/src/main/kotlin/dev/note11/flutter_naver_map/flutter_naver_map/applier/ApplyUtil.kt

Lines changed: 59 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,24 @@ import dev.note11.flutter_naver_map.flutter_naver_map.applier.option.NaverMapOpt
44
import dev.note11.flutter_naver_map.flutter_naver_map.view.NaverMapView
55

66
internal object ApplyUtil {
7-
internal fun <A : NaverMapOptionApplier> A.applyOptions(args: Map<String, Any>): A {
7+
internal fun <A : NaverMapOptionApplier> A.applyOptions(args: Map<String, Any?>): A {
88
for ((funcName, arg) in args) {
9-
try {
10-
val func = optionApplyFuncMap[funcName]?.invoke(this)
11-
func?.invoke(arg)
12-
} catch (e: NullPointerException) {
9+
val func = optionApplyFuncMap[funcName]?.invoke(this)
10+
if (func != null) {
11+
try {
12+
func.invoke(arg)
13+
} catch (e: NullPointerException) {
14+
throw IllegalArgumentException(
15+
"Invalid argument for \"$funcName\". " +
16+
"Please check the type of the argument: $arg. this option really can be null?"
17+
)
18+
} catch (e: Exception) {
19+
throw RuntimeException(
20+
"Failed to apply option \"$funcName\" with argument: $arg",
21+
e
22+
)
23+
}
24+
} else {
1325
throw NoSuchMethodException(
1426
"No such method \"$funcName\". " +
1527
"Please check the handling of this method."
@@ -19,40 +31,50 @@ internal object ApplyUtil {
1931
return this
2032
}
2133

22-
private val optionApplyFuncMap: Map<String, ((NaverMapOptionApplier) -> ((Any) -> Unit))> =
34+
private val optionApplyFuncMap: Map<String, ((NaverMapOptionApplier) -> ((Any?) -> Unit))> =
2335
mapOf(
24-
"initialCameraPosition" to { it::setInitialCameraPosition },
36+
"initialCameraPosition" to nonNullFunc { it::setInitialCameraPosition },
2537
"extent" to { it::setExtent },
26-
"mapType" to { it::setMapType },
27-
"liteModeEnable" to { it::setLiteModeEnable },
28-
"nightModeEnable" to { it::setNightModeEnable },
29-
"indoorEnable" to { it::setIndoorEnable },
30-
"activeLayerGroups" to { it::setActiveLayerGroups },
31-
"buildingHeight" to { it::setBuildingHeight },
32-
"lightness" to { it::setLightness },
33-
"symbolScale" to { it::setSymbolScale },
34-
"symbolPerspectiveRatio" to { it::setSymbolPerspectiveRatio },
35-
"indoorFocusRadius" to { it::setIndoorFocusRadius },
36-
"pickTolerance" to { it::setPickTolerance },
37-
"rotationGesturesEnable" to { it::setRotationGesturesEnable },
38-
"scrollGesturesEnable" to { it::setScrollGesturesEnable },
39-
"tiltGesturesEnable" to { it::setTiltGesturesEnable },
40-
"zoomGesturesEnable" to { it::setZoomGesturesEnable },
41-
"stopGesturesEnable" to { it::setStopGesturesEnable },
42-
"scrollGesturesFriction" to { it::setScrollGesturesFriction },
43-
"zoomGesturesFriction" to { it::setZoomGesturesFriction },
44-
"rotationGesturesFriction" to { it::setRotationGesturesFriction },
38+
"mapType" to nonNullFunc { it::setMapType },
39+
"liteModeEnable" to nonNullFunc { it::setLiteModeEnable },
40+
"nightModeEnable" to nonNullFunc { it::setNightModeEnable },
41+
"indoorEnable" to nonNullFunc { it::setIndoorEnable },
42+
"activeLayerGroups" to nonNullFunc { it::setActiveLayerGroups },
43+
"buildingHeight" to nonNullFunc { it::setBuildingHeight },
44+
"lightness" to nonNullFunc { it::setLightness },
45+
"symbolScale" to nonNullFunc { it::setSymbolScale },
46+
"symbolPerspectiveRatio" to nonNullFunc { it::setSymbolPerspectiveRatio },
47+
"indoorFocusRadius" to nonNullFunc { it::setIndoorFocusRadius },
48+
"pickTolerance" to nonNullFunc { it::setPickTolerance },
49+
"rotationGesturesEnable" to nonNullFunc { it::setRotationGesturesEnable },
50+
"scrollGesturesEnable" to nonNullFunc { it::setScrollGesturesEnable },
51+
"tiltGesturesEnable" to nonNullFunc { it::setTiltGesturesEnable },
52+
"zoomGesturesEnable" to nonNullFunc { it::setZoomGesturesEnable },
53+
"stopGesturesEnable" to nonNullFunc { it::setStopGesturesEnable },
54+
"scrollGesturesFriction" to nonNullFunc { it::setScrollGesturesFriction },
55+
"zoomGesturesFriction" to nonNullFunc { it::setZoomGesturesFriction },
56+
"rotationGesturesFriction" to nonNullFunc { it::setRotationGesturesFriction },
4557
"consumeSymbolTapEvents" to { { /** @see NaverMapView.setMapTapListener method */ } },
46-
"scaleBarEnable" to { it::setScaleBarEnable },
47-
"indoorLevelPickerEnable" to { it::setIndoorLevelPickerEnable },
48-
"locationButtonEnable" to { it::setLocationButtonEnable },
49-
"logoClickEnable" to { it::setLogoClickEnable },
50-
"logoAlign" to { it::setLogoAlign },
51-
"logoMargin" to { it::setLogoMargin },
52-
"contentPadding" to { it::setContentPadding },
53-
"minZoom" to { it::setMinZoom },
54-
"maxZoom" to { it::setMaxZoom },
55-
"maxTilt" to { it::setMaxTilt },
56-
"locale" to { it::setLocale },
58+
"scaleBarEnable" to nonNullFunc { it::setScaleBarEnable },
59+
"indoorLevelPickerEnable" to nonNullFunc { it::setIndoorLevelPickerEnable },
60+
"locationButtonEnable" to nonNullFunc { it::setLocationButtonEnable },
61+
"logoClickEnable" to nonNullFunc { it::setLogoClickEnable },
62+
"logoAlign" to nonNullFunc { it::setLogoAlign },
63+
"logoMargin" to nonNullFunc { it::setLogoMargin },
64+
"contentPadding" to nonNullFunc { it::setContentPadding },
65+
"minZoom" to nonNullFunc { it::setMinZoom },
66+
"maxZoom" to nonNullFunc { it::setMaxZoom },
67+
"maxTilt" to nonNullFunc { it::setMaxTilt },
68+
"locale" to nonNullFunc { it::setLocale },
69+
"customStyleId" to { it::setCustomStyleId },
5770
)
71+
72+
private fun nonNullFunc(ev: ((NaverMapOptionApplier) -> ((Any) -> Unit))): ((NaverMapOptionApplier) -> ((Any?) -> Unit)) {
73+
return { applier ->
74+
{ arg ->
75+
if (arg == null) throw NullPointerException("Argument cannot be null for this option.")
76+
ev.invoke(applier).invoke(arg)
77+
}
78+
}
79+
}
5880
}

android/src/main/kotlin/dev/note11/flutter_naver_map/flutter_naver_map/applier/option/NaverMapApplierImpl.kt

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,13 @@ import dev.note11.flutter_naver_map.flutter_naver_map.util.DisplayUtil
1515

1616
class NaverMapApplierImpl(
1717
private val naverMap: NaverMap,
18+
private val customStyleCallback: NaverMap.OnCustomStyleLoadCallback? = null,
1819
) : NaverMapOptionApplier {
1920

2021
override fun setInitialCameraPosition(rawPosition: Any) = Unit
2122

22-
override fun setExtent(rawLatLngBounds: Any) {
23-
naverMap.extent = rawLatLngBounds.asLatLngBounds()
23+
override fun setExtent(rawLatLngBounds: Any?) {
24+
naverMap.extent = rawLatLngBounds?.asLatLngBounds()
2425
}
2526

2627
override fun setMapType(rawMapType: Any) {
@@ -154,4 +155,8 @@ class NaverMapApplierImpl(
154155
val nLocale = NLocale.fromMessageable(rawLocale)
155156
naverMap.locale = nLocale?.toLocale()
156157
}
158+
159+
override fun setCustomStyleId(rawCustomStyleId: Any?) {
160+
naverMap.setCustomStyleId(rawCustomStyleId?.toString(), customStyleCallback)
161+
}
157162
}

android/src/main/kotlin/dev/note11/flutter_naver_map/flutter_naver_map/applier/option/NaverMapOptionApplier.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ package dev.note11.flutter_naver_map.flutter_naver_map.applier.option
22

33
internal interface NaverMapOptionApplier {
44
fun setInitialCameraPosition(rawPosition: Any)
5-
fun setExtent(rawLatLngBounds: Any)
5+
fun setExtent(rawLatLngBounds: Any?)
66
fun setMapType(rawMapType: Any)
77
fun setLiteModeEnable(rawEnable: Any)
88
fun setNightModeEnable(rawEnable: Any)
@@ -33,4 +33,5 @@ internal interface NaverMapOptionApplier {
3333
fun setMaxZoom(rawLevel: Any)
3434
fun setMaxTilt(rawTilt: Any)
3535
fun setLocale(rawLocale: Any)
36+
fun setCustomStyleId(rawCustomStyleId: Any?)
3637
}

android/src/main/kotlin/dev/note11/flutter_naver_map/flutter_naver_map/applier/option/NaverMapOptionApplierImpl.kt

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@ class NaverMapOptionApplierImpl(
2222
options.camera(cameraPosition)
2323
}
2424

25-
override fun setExtent(rawLatLngBounds: Any) {
26-
options.extent(rawLatLngBounds.asLatLngBounds())
25+
override fun setExtent(rawLatLngBounds: Any?) {
26+
options.extent(rawLatLngBounds?.asLatLngBounds())
2727
}
2828

2929
override fun setMapType(rawMapType: Any) {
@@ -155,4 +155,8 @@ class NaverMapOptionApplierImpl(
155155
val nLocale = NLocale.fromMessageable(rawLocale)
156156
options.locale(nLocale?.toLocale())
157157
}
158+
159+
override fun setCustomStyleId(rawCustomStyleId: Any?) {
160+
options.customStyleId(rawCustomStyleId?.toString())
161+
}
158162
}

android/src/main/kotlin/dev/note11/flutter_naver_map/flutter_naver_map/controller/NaverMapControlHandler.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,7 @@ internal interface NaverMapControlHandler {
159159

160160
fun forceRefresh(onSuccess: () -> Unit)
161161

162-
fun updateOptions(rawOptions: Map<String, Any>, onSuccess: () -> Unit)
162+
fun updateOptions(rawOptions: Map<String, Any?>, onSuccess: () -> Unit)
163163

164164
fun updateClusteringOptions(rawOptions: Map<String, Any>, onSuccess: () -> Unit)
165165

android/src/main/kotlin/dev/note11/flutter_naver_map/flutter_naver_map/controller/NaverMapControlSender.kt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,10 @@ internal interface NaverMapControlSender {
1717
fun onCameraIdle()
1818

1919
fun onSelectedIndoorChanged(selectedIndoor: IndoorSelection?)
20+
21+
fun onCustomStyleLoaded()
22+
23+
fun onCustomStyleLoadFailed(exception: Exception)
24+
25+
fun dispose()
2026
}

android/src/main/kotlin/dev/note11/flutter_naver_map/flutter_naver_map/controller/NaverMapController.kt

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import com.naver.maps.geometry.LatLng
77
import com.naver.maps.map.CameraUpdate
88
import com.naver.maps.map.LocationTrackingMode
99
import com.naver.maps.map.NaverMap
10+
import com.naver.maps.map.NaverMapSdk
1011
import com.naver.maps.map.Projection
1112
import com.naver.maps.map.Symbol
1213
import com.naver.maps.map.app.LegalNoticeActivity
@@ -20,8 +21,8 @@ import dev.note11.flutter_naver_map.flutter_naver_map.converter.MapTypeConverter
2021
import dev.note11.flutter_naver_map.flutter_naver_map.converter.MapTypeConverter.toMessageableString
2122
import dev.note11.flutter_naver_map.flutter_naver_map.model.enum.NOverlayType
2223
import dev.note11.flutter_naver_map.flutter_naver_map.model.base.NPoint
24+
import dev.note11.flutter_naver_map.flutter_naver_map.model.exception.NFlutterException
2325
import dev.note11.flutter_naver_map.flutter_naver_map.model.map.NaverMapViewOptions
24-
import dev.note11.flutter_naver_map.flutter_naver_map.model.map.info.NClusterableMarkerInfo
2526
import dev.note11.flutter_naver_map.flutter_naver_map.model.map.info.NOverlayInfo
2627
import dev.note11.flutter_naver_map.flutter_naver_map.model.map.info.NPickableInfo
2728
import dev.note11.flutter_naver_map.flutter_naver_map.model.map.info.NSymbolInfo
@@ -230,9 +231,10 @@ internal class NaverMapController(
230231
onSuccess()
231232
}
232233

233-
override fun updateOptions(rawOptions: Map<String, Any>, onSuccess: () -> Unit) {
234-
naverMapViewOptions =
235-
NaverMapViewOptions.updateNaverMapFromMessageable(naverMap, rawOptions)
234+
override fun updateOptions(rawOptions: Map<String, Any?>, onSuccess: () -> Unit) {
235+
naverMapViewOptions = NaverMapViewOptions.updateNaverMapFromMessageable(
236+
naverMap, rawOptions, getCustomStyleCallback()
237+
)
236238
onSuccess()
237239
}
238240

@@ -307,11 +309,32 @@ internal class NaverMapController(
307309
channel.invokeMethod("onSelectedIndoorChanged", selectedIndoor?.toMessageable())
308310
}
309311

312+
override fun onCustomStyleLoaded() {
313+
channel.invokeMethod("onCustomStyleLoaded", null)
314+
}
315+
316+
override fun onCustomStyleLoadFailed(exception: Exception) {
317+
val flutterError = when (exception) {
318+
is NaverMapSdk.AuthFailedException ->
319+
if (exception.errorCode == "400") NFlutterException(
320+
code = "400",
321+
message = "Invalid custom style ID: ${exception.message}"
322+
)
323+
else NFlutterException(
324+
code = exception.errorCode,
325+
message = "Custom style load failed: ${exception.message}"
326+
)
327+
/// in iOS, 900 is no handling error code, so we use 900 here too
328+
else -> NFlutterException(code = "900", message = exception.message)
329+
}
330+
channel.invokeMethod("onCustomStyleLoadFailed", flutterError.toMessageable())
331+
}
332+
310333
/*
311334
--- remove ---
312335
*/
313336

314-
fun remove() {
337+
override fun dispose() {
315338
channel.setMethodCallHandler(null)
316339
clusteringController.dispose()
317340
overlayController.remove()
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package dev.note11.flutter_naver_map.flutter_naver_map.controller
2+
3+
import com.naver.maps.map.NaverMap.OnCustomStyleLoadCallback
4+
5+
internal fun NaverMapControlSender.getCustomStyleCallback(): OnCustomStyleLoadCallback =
6+
object : OnCustomStyleLoadCallback {
7+
override fun onCustomStyleLoaded() = this@getCustomStyleCallback.onCustomStyleLoaded()
8+
9+
override fun onCustomStyleLoadFailed(exception: Exception) =
10+
this@getCustomStyleCallback.onCustomStyleLoadFailed(exception)
11+
}

android/src/main/kotlin/dev/note11/flutter_naver_map/flutter_naver_map/converter/DefaultTypeConverter.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,13 @@ internal object DefaultTypeConverter {
66
fun Any.asFloat(): Float = if (this is Double) toFloat() else this as Float
77
fun Any.asInt(): Int = if (this is Long) toInt() else this as Int
88
fun Any.asLong(): Long = if (this is Int) toLong() else this as Long
9+
910
@Suppress("UNCHECKED_CAST")
1011
fun Any.asMap(): Map<String, Any> = this as Map<String, Any>
1112

13+
@Suppress("UNCHECKED_CAST")
14+
fun Any.asNullableMap(): Map<String, Any?> = this as Map<String, Any?>
15+
1216
@Suppress("UNCHECKED_CAST")
1317
fun Any.asStringMap(): Map<String, String> = this as Map<String, String>
1418

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package dev.note11.flutter_naver_map.flutter_naver_map.model.exception
2+
3+
data class NFlutterException(
4+
val code: String,
5+
val message: String?,
6+
) {
7+
fun toMessageable(): Map<String, Any?> {
8+
return mapOf(
9+
"code" to code,
10+
"message" to message,
11+
)
12+
}
13+
}

0 commit comments

Comments
 (0)