All Downloads are FREE. Search and download functionalities are using the official Maven repository.

commonMain.tech.skot.libraries.map.SKMap.kt Maven / Gradle / Ivy

The newest version!
package tech.skot.libraries.map

import tech.skot.core.components.SKComponent
import tech.skot.core.di.get
import tech.skot.core.view.SKPermission
import tech.skot.libraries.map.di.skmapViewInjector
import tech.skot.libraries.map.view.Permissions

/**
 * # SKMap
 * ## A map component
 * Based on google Map
 * no iOS version at this time
 *
 * @param mapInteractionSettingsInitial the [SKMapVC.MapInteractionSettings] to use for map. Any of [MapNormalInteractionSettings][SKMapVC.MapNormalInteractionSettings], [MapClusteringInteractionSettings][SKMapVC.MapClusteringInteractionSettings] or [MapCustomInteractionSettings][SKMapVC.MapCustomInteractionSettings]
 * @param markersInitial the [markers][SKMapVC.Marker] shown on the map
 * @param selectedMarkerInitial the first marker selected
 * @param selectMarkerOnClickInitial indicate if a marker must be selected when clicked
 * @param unselectMarkerOnMapClickInitial indicate if the selected marker must be unselect when map is clicked
 * @param onMarkerClickedInitial nullable function type which allow to obtain the clicked marker
 * @param onMapClickedInitial nullable function type which allow to obtain the map click position
 * @param onMarkerSelectedInitial nullable function type which allow to obtain the selected marker, or null on unselect
 * @param onMapBoundsChangeInitial nullable function type which allow to obtain the MapBounds on each change
 *
 */
@Suppress("unused")
class SKMap(
    mapInteractionSettingsInitial: SKMapVC.MapInteractionSettings = SKMapVC.MapNormalInteractionSettings,
    markersInitial: List = emptyList(),
    linesInitial: List = emptyList(),
    polygonsInitial: List = emptyList(),
    selectedMarkerInitial: SKMapVC.Marker? = null,
    selectMarkerOnClickInitial: Boolean = true,
    unselectMarkerOnMapClickInitial: Boolean = true,
    onMarkerClickedInitial: ((SKMapVC.Marker) -> Unit)? = null,
    onMapClickedInitial: ((LatLng) -> Unit)? = null,
    onMapLongClickedInitial: ((LatLng) -> Unit)? = null,
    onMarkerSelectedInitial: ((SKMapVC.Marker?) -> Unit)? = null,
    onMapBoundsChangeInitial: ((SKMapVC.LatLngBounds) -> Unit)? = null,
    showLogInitial: Boolean = false,
    mapTypeInitial: MapType = MapType.NORMAL

) : SKComponent() {

    private val declaredPermissionHelper: DeclaredPermissionHelper = get()
    private val permissions: Permissions = get()

    private val internalOnMapClicked: (LatLng) -> Unit = {
        onMapClicked?.invoke(it)
        if (internalView.unselectMarkerOnMapClick) {
            selectedMarker = null
        }
    }

    private val internalOnMapLongClicked: (LatLng) -> Unit = {
        onMapLongClicked?.invoke(it)
    }

    private val internalOnMarkerClicked: (SKMapVC.Marker) -> Unit = {
        onMarkerClicked?.invoke(it)
        it.onMarkerClick?.invoke()
        if (internalView.selectMarkerOnClick) {
            selectedMarker = it
        }
    }


    override val view: SKMapVC = skmapViewInjector.sKMap(
        mapInteractionSettingsInitial = mapInteractionSettingsInitial,
        markersInitial = markersInitial,
        linesInitial = linesInitial,
        polygonsInitial = polygonsInitial,
        selectedMarkerInitial = selectedMarkerInitial,
        selectMarkerOnClickInitial = selectMarkerOnClickInitial,
        unselectMarkerOnMapClickInitial = unselectMarkerOnMapClickInitial,
        onMarkerClickInitial = internalOnMarkerClicked,
        onMapClickedInitial = internalOnMapClicked,
        onMapLongClickedInitial = internalOnMapLongClicked,
        onMarkerSelectedInitial = onMarkerSelectedInitial,
        onMapBoundsChangeInitial = onMapBoundsChangeInitial,
        showLogInitial = showLogInitial,
        mapType = mapTypeInitial
    )

    var mapType: MapType
        get() = view.mapType
        set(value) {
            view.mapType = value
        }


    init {
        MapLogger.enabled = showLogInitial
    }

    private val internalView = view as InternalSKMapVC

    var mapInteractionSettings: SKMapVC.MapInteractionSettings
        get() = internalView.mapInteractionSettings
        set(value) {
            internalView.mapInteractionSettings = value
        }

    var selectMarkerOnClick: Boolean
        get() = internalView.selectMarkerOnClick
        set(value) {
            internalView.selectMarkerOnClick = value
        }

    /**
     * called on marker click
     * @see SKMapVC.Marker.onMarkerClick
     */
    @Suppress("unused")
    var onMarkerClicked: ((SKMapVC.Marker) -> Unit)? = onMarkerClickedInitial


    var showLog: Boolean
        get() = view.showLog
        set(value) {
            view.showLog = value
        }


    var unselectMarkerOnMapClick: Boolean
        get() = internalView.unselectMarkerOnMapClick
        set(value) {
            internalView.unselectMarkerOnMapClick = value
        }

    /**
     * called on map click
     */
    @Suppress("unused")
    var onMapClicked: ((LatLng) -> Unit)? = onMapClickedInitial

    /**
     * called on map long click
     */
    @Suppress("unused")
    var onMapLongClicked: ((LatLng) -> Unit)? = onMapLongClickedInitial


    var onMarkerSelected: ((SKMapVC.Marker?) -> Unit)?
        get() = internalView.onMarkerSelected
        set(value) {
            internalView.onMarkerSelected = value
        }

    /**
     * list of markers
     */
    @Suppress("unused")
    var markers: List
        get() = view.markers
        set(value) {
            view.markers = value
        }

    /**
     * list of lines
     */
    @Suppress("unused")
    var polylines: List
        get() = view.polylines
        set(value) {
            view.polylines = value
        }

    /**
     * list of lines
     */
    @Suppress("unused")
    var polygons: List
        get() = view.polygons
        set(value) {
            view.polygons = value
        }


    /**
     * current selected Marker, null if no marker selected
     */
    @Suppress("unused")
    var selectedMarker: SKMapVC.Marker?
        get() = internalView.selectedMarker
        set(value) {
            internalView.selectedMarker = value
            onMarkerSelected?.invoke(value)
        }

    /**
     *  called each time [MapBounds][SKMapVC.LatLngBounds] change (when mapview is idle)
     */
    @Suppress("unused")
    var onMapBoundsChange: ((MapBounds) -> Unit)?
        get() = view.onMapBoundsChange
        set(value) {
            view.onMapBoundsChange = value
        }


    /**
     * Show my location button
     * @param show : true to show my location button
     * @param askForPermissionIfNeeded true to ask for Permissions if needed. You can use it if permissions are not managed in screen
     * @param onPermissionError a callback fired if no location permissions is granted
     */
    @Suppress("unused")
    fun showMyLocationButton(
        show: Boolean = true,
        askForPermissionIfNeeded: Boolean = false,
        onPermissionError: (() -> Unit)? = null
    ) {
        val hasCoarsePermission =
            permissions.coarseLocation?.let { declaredPermissionHelper.isPermissionDeclaredForApp(it) }
                ?: false
        val hasFinePermission =
            permissions.fineLocation?.let { declaredPermissionHelper.isPermissionDeclaredForApp(it) }
                ?: false
        val myLocationPermission = mutableListOf()
        permissions.coarseLocation?.let { myLocationPermission.add(it) }
        permissions.fineLocation?.let { myLocationPermission.add(it) }


        val hasPermissionInManifest = hasCoarsePermission || hasFinePermission

        val hasAtLeastOnePermissionGranted = myLocationPermission.find {
            hasPermission(it)
        } != null

        MapLogger.d("has permission in Manifest : $hasPermissionInManifest")
        MapLogger.d("has permission Granted : $hasAtLeastOnePermissionGranted")
        MapLogger.d("askForPermissionIfNeeded : $askForPermissionIfNeeded")


        when {
            !hasPermissionInManifest -> {
                onPermissionError?.invoke()
            }
            hasAtLeastOnePermissionGranted -> {
                view.showMyLocationButton(show, onPermissionError)
            }
            askForPermissionIfNeeded -> {
                MapLogger.d("Request permission : ")
                requestPermissions(myLocationPermission) {
                    if (it.isNotEmpty()) {
                        MapLogger.d("Requested permission : Granted")
                        view.showMyLocationButton(show, onPermissionError)
                    } else {
                        MapLogger.d("Requested permission : Refused")
                        onPermissionError?.invoke()
                    }
                }
            }
            else -> {
                onPermissionError?.invoke()
            }
        }
    }


    /**
     * Show my location  on map
     * @param show : true to show my location on map
     * @param askForPermissionIfNeeded true to ask for Permissions if needed. You can use it if permissions are not managed in screen
     * @param onPermissionError a callback fired if no location permissions is granted
     */
    @Suppress("unused")
    fun showMyLocation(
        show: Boolean = true,
        askForPermissionIfNeeded: Boolean = false,
        onPermissionError: (() -> Unit)? = null
    ) {
        val hasCoarsePermission =
            permissions.coarseLocation?.let { declaredPermissionHelper.isPermissionDeclaredForApp(it) }
                ?: false
        val hasFinePermission =
            permissions.fineLocation?.let { declaredPermissionHelper.isPermissionDeclaredForApp(it) }
                ?: false
        val myLocationPermission = mutableListOf()
        permissions.coarseLocation?.let { myLocationPermission.add(it) }
        permissions.fineLocation?.let { myLocationPermission.add(it) }


        val hasPermissionInManifest = hasCoarsePermission || hasFinePermission

        val hasAtLeastOnePermissionGranted = myLocationPermission.find {
            hasPermission(it)
        } != null

        MapLogger.d("has permission in Manifest : $hasPermissionInManifest")
        MapLogger.d("has permission Granted : $hasAtLeastOnePermissionGranted")
        MapLogger.d("askForPermissionIfNeeded : $askForPermissionIfNeeded")


        when {
            !hasPermissionInManifest -> {
                onPermissionError?.invoke()
            }
            hasAtLeastOnePermissionGranted -> {
                view.showMyLocation(show, onPermissionError)
            }
            askForPermissionIfNeeded -> {
                MapLogger.d("Request permission : ")
                requestPermissions(myLocationPermission) {
                    if (it.isNotEmpty()) {
                        MapLogger.d("Requested permission : Granted")
                        view.showMyLocation(show, onPermissionError)
                    } else {
                        MapLogger.d("Requested permission : Refused")
                        onPermissionError?.invoke()
                    }
                }
            }
            else -> {
                onPermissionError?.invoke()
            }
        }
    }



    /**
     * get current MapBounds
     * @param onResult called once with current [MapBounds][SKMapVC.LatLngBounds]
     */
    @Suppress("unused")
    fun getMapBounds(onResult: (MapBounds) -> Unit) {
        view.getMapBounds(onResult)
    }

    /**
     * center map to show all markers currently added
     */
    @Suppress("unused")
    fun centerOnMarkers() {
        centerOnPositions(markers.toPositions())
    }

    /**
     * change Camera zoom level with or without animation
     * @param zoomLevel the zoomLevel to apply
     * @param animate true to animate zoom change
     */
    @Suppress("unused")
    fun setCameraZoom(zoomLevel: Float, animate: Boolean) {
        view.setCameraZoom(zoomLevel, animate)
    }

    /**
     * center map to show all positions
     */
    @Suppress("unused")
    fun centerOnPositions(positions: List) {
        view.centerOnPositions(positions)
    }

    /**
     *  function to call for moving camera on another location
     *  @param pos a [Pair] of [LatLng] representing the requested location for the center of the map
     *  @param zoomLevel a [Float] representing the zoom level to use
     *  @param animate true if the position change must be animated, false otherwise
     */
    @Suppress("unused")
    fun setCameraPosition(pos: LatLng, zoomLevel: Float, animate: Boolean = true) {
        view.setCameraPosition(pos, zoomLevel, animate)
    }

    /**
     * getCurrent user Location
     * @param onResult Lambda called with LatLng position in param
     */
    fun getCurrentLocation(onResult: (LatLng) -> Unit) {
        view.getCurrentLocation(onResult)
    }

    private fun List.toPositions(): List = map { it.position }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy