From 483f1d696ac5cca5907c448fbbfdba1dd8bdff86 Mon Sep 17 00:00:00 2001 From: Ivan Sablin Date: Tue, 29 Mar 2022 00:26:28 +0700 Subject: [PATCH 1/4] Add apple silicon simulator support --- build.gradle.kts | 27 +- gradle.properties | 11 + gradle/libs.versions.toml | 34 +- gradle/wrapper/gradle-wrapper.properties | 2 +- maps-build-logic/build.gradle.kts | 17 - .../kotlin/android-app-convention.gradle.kts | 32 -- .../kotlin/android-base-convention.gradle.kts | 14 - .../android-library-convention.gradle.kts | 13 - .../android-publication-convention.gradle.kts | 15 - .../main/kotlin/detekt-convention.gradle.kts | 15 - .../kotlin/javadoc-stub-convention.gradle.kts | 16 - ...ultiplatform-library-convention.gradle.kts | 18 - .../kotlin/publication-convention.gradle.kts | 75 --- maps-google/build.gradle.kts | 6 +- .../moko/maps/google/GoogleMapController.kt | 439 ++++++++++++++++++ .../icerock/moko/maps/google/GoogleMarker.kt | 43 ++ .../icerock/moko/maps/google/GooglePolygon.kt | 14 + .../icerock/moko/maps/google/GoogleRoute.kt | 23 + maps-mapbox/build.gradle.kts | 6 +- maps/build.gradle.kts | 6 +- sample/android-app/build.gradle.kts | 13 +- sample/mpp-library/build.gradle.kts | 12 +- settings.gradle.kts | 3 +- 23 files changed, 590 insertions(+), 264 deletions(-) delete mode 100644 maps-build-logic/build.gradle.kts delete mode 100644 maps-build-logic/src/main/kotlin/android-app-convention.gradle.kts delete mode 100644 maps-build-logic/src/main/kotlin/android-base-convention.gradle.kts delete mode 100644 maps-build-logic/src/main/kotlin/android-library-convention.gradle.kts delete mode 100644 maps-build-logic/src/main/kotlin/android-publication-convention.gradle.kts delete mode 100644 maps-build-logic/src/main/kotlin/detekt-convention.gradle.kts delete mode 100644 maps-build-logic/src/main/kotlin/javadoc-stub-convention.gradle.kts delete mode 100644 maps-build-logic/src/main/kotlin/multiplatform-library-convention.gradle.kts delete mode 100644 maps-build-logic/src/main/kotlin/publication-convention.gradle.kts create mode 100644 maps-google/src/iosSimulatorArm64Main/kotlin/dev/icerock/moko/maps/google/GoogleMapController.kt create mode 100644 maps-google/src/iosSimulatorArm64Main/kotlin/dev/icerock/moko/maps/google/GoogleMarker.kt create mode 100644 maps-google/src/iosSimulatorArm64Main/kotlin/dev/icerock/moko/maps/google/GooglePolygon.kt create mode 100644 maps-google/src/iosSimulatorArm64Main/kotlin/dev/icerock/moko/maps/google/GoogleRoute.kt diff --git a/build.gradle.kts b/build.gradle.kts index b663b04..0c957e9 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,18 +1,26 @@ /* * Copyright 2019 IceRock MAG Inc. Use of this source code is governed by the Apache 2.0 license. */ -import org.gradle.api.internal.artifacts.DefaultModuleVersionSelector buildscript { repositories { mavenCentral() + mavenLocal() + google() + gradlePluginPortal() } + dependencies { - classpath(":maps-build-logic") - classpath("dev.icerock.moko:resources-generator:0.16.1") - classpath("org.jetbrains.kotlin:kotlin-serialization:1.5.20") + classpath(libs.kotlinGradlePlugin) + classpath(libs.androidGradlePlugin) + classpath(libs.googleServicesGradlePlugin) + classpath(libs.firebaseGradlePlugin) + classpath(libs.mokoGradlePlugin) + classpath(libs.mobileMultiplatformGradlePlugin) + classpath(libs.kotlinSerializationGradlePlugin) + classpath(libs.mokoResourcesGeneratorGradlePlugin) } } @@ -21,18 +29,9 @@ allprojects { group = "dev.icerock.moko" version = libs.versions.mokoMapsVersion.get() } - configurations.configureEach { - resolutionStrategy { - val coroutines: MinimalExternalModuleDependency = rootProject.libs.coroutines.get() - val forcedCoroutines: ModuleVersionSelector = DefaultModuleVersionSelector.newSelector( - coroutines.module, - coroutines.versionConstraint.requiredVersion - ) - force(forcedCoroutines) - } - } } + tasks.register("clean", Delete::class).configure { delete(rootProject.buildDir) } diff --git a/gradle.properties b/gradle.properties index 6effeb7..5fab2e0 100755 --- a/gradle.properties +++ b/gradle.properties @@ -12,6 +12,17 @@ android.enableJetifier=true xcodeproj=./sample/ios-app +moko.android.targetSdk=31 +moko.android.compileSdk=31 +moko.android.minSdk=16 + +moko.publish.name=MOKO maps +moko.publish.description=description +moko.publish.repo.org=icerockdev +moko.publish.repo.name=moko-maps +moko.publish.license=Apache-2.0 +moko.publish.developers=alex009|Aleksey Mikhailov|Aleksey.Mikhailov@icerockdev.com + mobile.multiplatform.iosTargetWarning=false mobile.multiplatform.podsProject=sample/ios-app/Pods/Pods.xcodeproj kotlin.mpp.stability.nowarn=true diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 310b673..46cd001 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,5 +1,5 @@ [versions] -kotlinVersion = "1.5.20" +kotlinVersion = "1.6.10" lifecycleVersion = "2.2.0" androidAppCompatVersion = "1.2.0" espressoCoreVersion = "3.2.0" @@ -13,16 +13,16 @@ mapboxNavigationVersion = "1.5.1" mapboxAnnotationVersion = "0.9.0" mapboxServicesVersion = "5.8.0" multidexVersion = "2.0.1" -kotlinxSerializationVersion = "1.2.1" -coroutinesVersion = "1.5.0-native-mt" -ktorClientVersion = "1.6.0" -mokoGraphicsVersion = "0.7.0" -mokoParcelizeVersion = "0.7.1" -mokoResourcesVersion = "0.16.1" -mokoMvvmVersion = "0.11.0" -mokoGeoVersion = "0.4.0" -mokoPermissionsVersion = "0.10.1" -mokoMapsVersion = "0.6.0" +kotlinxSerializationVersion = "1.3.2" +coroutinesVersion = "1.6.0-native-mt" +ktorClientVersion = "1.6.7" +mokoGraphicsVersion = "0.9.0" +mokoParcelizeVersion = "0.8.0" +mokoResourcesVersion = "0.18.0" +mokoMvvmVersion = "0.12.0" +mokoGeoVersion = "0.5.0" +mokoPermissionsVersion = "0.11.0" +mokoMapsVersion = "0.7.0" [libraries] appCompat = { module = "androidx.appcompat:appcompat", version.ref = "androidAppCompatVersion" } @@ -42,7 +42,7 @@ ktorClient = { module = "io.ktor:ktor-client-core", version.ref = "ktorClientVer mokoResources = { module = "dev.icerock.moko:resources", version.ref = "mokoResourcesVersion" } mokoParcelize = { module = "dev.icerock.moko:parcelize", version.ref = "mokoParcelizeVersion" } mokoGraphics = { module = "dev.icerock.moko:graphics", version.ref = "mokoGraphicsVersion" } -mokoMvvm = { module = "dev.icerock.moko:mvvm", version.ref = "mokoMvvmVersion" } +mokoMvvm = { module = "dev.icerock.moko:mvvm-core", version.ref = "mokoMvvmVersion" } mokoGeo = { module = "dev.icerock.moko:geo", version.ref = "mokoGeoVersion" } mokoPermissions = { module = "dev.icerock.moko:permissions", version.ref = "mokoPermissionsVersion" } mokoMaps = { module = "dev.icerock.moko:maps", version.ref = "mokoMapsVersion" } @@ -51,3 +51,13 @@ mokoMapsMapbox = { module = "dev.icerock.moko:maps-mapbox", version.ref = "mokoM kotlinTest = { module = "org.jetbrains.kotlin:kotlin-test-common", version.ref = "kotlinVersion" } ktorClientIos = { module = "io.ktor:ktor-client-ios", version.ref = "ktorClientVersion" } + +kotlinGradlePlugin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlinVersion" } +androidGradlePlugin = { module = "com.android.tools.build:gradle", version = "7.0.4" } +googleServicesGradlePlugin = { module = "com.google.gms:google-services", version = "4.3.8" } +firebaseGradlePlugin = { module = "com.google.firebase:firebase-crashlytics-gradle", version = "2.2.0" } +mokoGradlePlugin = { module = "dev.icerock.moko:moko-gradle-plugin", version = "0.1.0" } +mobileMultiplatformGradlePlugin = { module = "dev.icerock:mobile-multiplatform", version = "0.14.1" } +mokoResourcesGeneratorGradlePlugin = { module = "dev.icerock.moko:resources-generator", version.ref = "mokoResourcesVersion" } +kotlinSerializationGradlePlugin = { module = "org.jetbrains.kotlin:kotlin-serialization", version.ref = "kotlinVersion" } + diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 0f80bbf..00e33ed 100755 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.1-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/maps-build-logic/build.gradle.kts b/maps-build-logic/build.gradle.kts deleted file mode 100644 index 84cbae3..0000000 --- a/maps-build-logic/build.gradle.kts +++ /dev/null @@ -1,17 +0,0 @@ -plugins { - `kotlin-dsl` -} - -repositories { - mavenCentral() - google() - - gradlePluginPortal() -} - -dependencies { - api("dev.icerock:mobile-multiplatform:0.12.0") - api("org.jetbrains.kotlin:kotlin-gradle-plugin:1.5.20") - api("com.android.tools.build:gradle:4.2.1") - api("io.gitlab.arturbosch.detekt:detekt-gradle-plugin:1.15.0") -} diff --git a/maps-build-logic/src/main/kotlin/android-app-convention.gradle.kts b/maps-build-logic/src/main/kotlin/android-app-convention.gradle.kts deleted file mode 100644 index 2859874..0000000 --- a/maps-build-logic/src/main/kotlin/android-app-convention.gradle.kts +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright 2021 IceRock MAG Inc. Use of this source code is governed by the Apache 2.0 license. - */ - -plugins { - id("com.android.application") - id("android-base-convention") - id("kotlin-android") -} - -android { - dexOptions { - javaMaxHeapSize = "2g" - } - - buildTypes { - getByName("release") { - isMinifyEnabled = true - proguardFiles(getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro") - } - getByName("debug") { - isDebuggable = true - applicationIdSuffix = ".debug" - } - } - - packagingOptions { - exclude("META-INF/*.kotlin_module") - exclude("META-INF/AL2.0") - exclude("META-INF/LGPL2.1") - } -} diff --git a/maps-build-logic/src/main/kotlin/android-base-convention.gradle.kts b/maps-build-logic/src/main/kotlin/android-base-convention.gradle.kts deleted file mode 100644 index bfd44d3..0000000 --- a/maps-build-logic/src/main/kotlin/android-base-convention.gradle.kts +++ /dev/null @@ -1,14 +0,0 @@ -/* - * Copyright 2021 IceRock MAG Inc. Use of this source code is governed by the Apache 2.0 license. - */ - -import com.android.build.gradle.BaseExtension - -configure { - compileSdkVersion(30) - - defaultConfig { - minSdkVersion(21) - targetSdkVersion(30) - } -} diff --git a/maps-build-logic/src/main/kotlin/android-library-convention.gradle.kts b/maps-build-logic/src/main/kotlin/android-library-convention.gradle.kts deleted file mode 100644 index c435401..0000000 --- a/maps-build-logic/src/main/kotlin/android-library-convention.gradle.kts +++ /dev/null @@ -1,13 +0,0 @@ -/* - * Copyright 2021 IceRock MAG Inc. Use of this source code is governed by the Apache 2.0 license. - */ - -plugins { - id("com.android.library") - id("kotlin-android") - id("android-base-convention") -} - -android { - sourceSets.all { java.srcDir("src/$name/kotlin") } -} diff --git a/maps-build-logic/src/main/kotlin/android-publication-convention.gradle.kts b/maps-build-logic/src/main/kotlin/android-publication-convention.gradle.kts deleted file mode 100644 index b4dae9e..0000000 --- a/maps-build-logic/src/main/kotlin/android-publication-convention.gradle.kts +++ /dev/null @@ -1,15 +0,0 @@ -/* - * Copyright 2021 IceRock MAG Inc. Use of this source code is governed by the Apache 2.0 license. - */ - -plugins { - id("publication-convention") -} - -afterEvaluate { - publishing.publications { - create("release", MavenPublication::class.java) { - from(components.getByName("release")) - } - } -} diff --git a/maps-build-logic/src/main/kotlin/detekt-convention.gradle.kts b/maps-build-logic/src/main/kotlin/detekt-convention.gradle.kts deleted file mode 100644 index 68343a8..0000000 --- a/maps-build-logic/src/main/kotlin/detekt-convention.gradle.kts +++ /dev/null @@ -1,15 +0,0 @@ -/* - * Copyright 2021 IceRock MAG Inc. Use of this source code is governed by the Apache 2.0 license. - */ - -plugins { - id("io.gitlab.arturbosch.detekt") -} - -detekt { - input.setFrom("src/commonMain/kotlin", "src/androidMain/kotlin", "src/iosMain/kotlin", "src/main/kotlin") -} - -dependencies { - "detektPlugins"("io.gitlab.arturbosch.detekt:detekt-formatting:1.15.0") -} diff --git a/maps-build-logic/src/main/kotlin/javadoc-stub-convention.gradle.kts b/maps-build-logic/src/main/kotlin/javadoc-stub-convention.gradle.kts deleted file mode 100644 index a1a2691..0000000 --- a/maps-build-logic/src/main/kotlin/javadoc-stub-convention.gradle.kts +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Copyright 2021 IceRock MAG Inc. Use of this source code is governed by the Apache 2.0 license. - */ - -plugins { - id("org.gradle.maven-publish") -} - -val javadocJar by tasks.registering(Jar::class) { - archiveClassifier.set("javadoc") -} - -publishing.publications.withType { - // Stub javadoc.jar artifact - artifact(javadocJar.get()) -} diff --git a/maps-build-logic/src/main/kotlin/multiplatform-library-convention.gradle.kts b/maps-build-logic/src/main/kotlin/multiplatform-library-convention.gradle.kts deleted file mode 100644 index 347106b..0000000 --- a/maps-build-logic/src/main/kotlin/multiplatform-library-convention.gradle.kts +++ /dev/null @@ -1,18 +0,0 @@ -/* - * Copyright 2021 IceRock MAG Inc. Use of this source code is governed by the Apache 2.0 license. - */ - -plugins { - id("com.android.library") - id("org.jetbrains.kotlin.multiplatform") - id("android-base-convention") - id("dev.icerock.mobile.multiplatform.android-manifest") -} - -kotlin { - ios() - android { - publishLibraryVariants("release", "debug") - } -} - diff --git a/maps-build-logic/src/main/kotlin/publication-convention.gradle.kts b/maps-build-logic/src/main/kotlin/publication-convention.gradle.kts deleted file mode 100644 index 46eeeca..0000000 --- a/maps-build-logic/src/main/kotlin/publication-convention.gradle.kts +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright 2021 IceRock MAG Inc. Use of this source code is governed by the Apache 2.0 license. - */ - -import java.util.Base64 - -plugins { - id("javadoc-stub-convention") - id("org.gradle.maven-publish") - id("signing") -} - -publishing { - repositories.maven("https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/") { - name = "OSSRH" - - credentials { - username = System.getenv("OSSRH_USER") - password = System.getenv("OSSRH_KEY") - } - } - - publications.withType { - // Provide artifacts information requited by Maven Central - pom { - name.set("MOKO maps") - description.set("Control your map from common code for mobile (android & ios) Kotlin Multiplatform development") - url.set("https://github.com/icerockdev/moko-maps") - licenses { - license { - name.set("Apache-2.0") - distribution.set("repo") - url.set("https://github.com/icerockdev/moko-maps/blob/master/LICENSE.md") - } - } - - developers { - developer { - id.set("Alex009") - name.set("Aleksey Mikhailov") - email.set("aleksey.mikhailov@icerockdev.com") - } - developer { - id.set("prokopishin") - name.set("Nikita Prokopishin") - email.set("nprokopishin@icerockdev.com") - } - developer { - id.set("Dorofeev") - name.set("Andrey Dorofeev") - email.set("adorofeev@icerockdev.com") - } - } - - scm { - connection.set("scm:git:ssh://github.com/icerockdev/moko-maps.git") - developerConnection.set("scm:git:ssh://github.com/icerockdev/moko-maps.git") - url.set("https://github.com/icerockdev/moko-maps") - } - } - } -} - - -signing { - val signingKeyId: String? = System.getenv("SIGNING_KEY_ID") - val signingPassword: String? = System.getenv("SIGNING_PASSWORD") - val signingKey: String? = System.getenv("SIGNING_KEY")?.let { base64Key -> - String(Base64.getDecoder().decode(base64Key)) - } - if (signingKeyId != null) { - useInMemoryPgpKeys(signingKeyId, signingKey, signingPassword) - sign(publishing.publications) - } -} diff --git a/maps-google/build.gradle.kts b/maps-google/build.gradle.kts index 20583c1..d11b91d 100644 --- a/maps-google/build.gradle.kts +++ b/maps-google/build.gradle.kts @@ -3,9 +3,11 @@ */ plugins { - id("multiplatform-library-convention") id("dev.icerock.mobile.multiplatform.android-manifest") - id("publication-convention") + id("dev.icerock.moko.gradle.multiplatform.mobile") + id("dev.icerock.moko.gradle.publication") + id("dev.icerock.moko.gradle.stub.javadoc") + id("dev.icerock.moko.gradle.detekt") id("kotlin-parcelize") id("kotlin-kapt") id("kotlinx-serialization") diff --git a/maps-google/src/iosSimulatorArm64Main/kotlin/dev/icerock/moko/maps/google/GoogleMapController.kt b/maps-google/src/iosSimulatorArm64Main/kotlin/dev/icerock/moko/maps/google/GoogleMapController.kt new file mode 100644 index 0000000..c0310a5 --- /dev/null +++ b/maps-google/src/iosSimulatorArm64Main/kotlin/dev/icerock/moko/maps/google/GoogleMapController.kt @@ -0,0 +1,439 @@ +/* + * Copyright 2019 IceRock MAG Inc. Use of this source code is governed by the Apache 2.0 license. + */ + +package dev.icerock.moko.maps.google + +import cocoapods.GoogleMaps.GMSAddress +import cocoapods.GoogleMaps.GMSCameraPosition +import cocoapods.GoogleMaps.GMSCoordinateBounds +import cocoapods.GoogleMaps.GMSGeocoder +import cocoapods.GoogleMaps.GMSMapView +import cocoapods.GoogleMaps.GMSMapViewDelegateProtocol +import cocoapods.GoogleMaps.GMSMarker +import cocoapods.GoogleMaps.GMSMutablePath +import cocoapods.GoogleMaps.GMSPath +import cocoapods.GoogleMaps.GMSPolygon +import cocoapods.GoogleMaps.GMSPolyline +import cocoapods.GoogleMaps.animateToCameraPosition +import cocoapods.GoogleMaps.animateToZoom +import cocoapods.GoogleMaps.create +import cocoapods.GoogleMaps.kGMSMaxZoomLevel +import cocoapods.GoogleMaps.kGMSMinZoomLevel +import dev.icerock.moko.geo.LatLng +import dev.icerock.moko.graphics.Color +import dev.icerock.moko.graphics.toUIColor +import dev.icerock.moko.maps.LineType +import dev.icerock.moko.maps.MapAddress +import dev.icerock.moko.maps.MapController +import dev.icerock.moko.maps.MapElement +import dev.icerock.moko.maps.Marker +import dev.icerock.moko.maps.ZoomConfig +import dev.icerock.moko.resources.ImageResource +import io.ktor.client.HttpClient +import io.ktor.client.call.ReceivePipelineException +import io.ktor.client.request.HttpRequestBuilder +import io.ktor.client.request.request +import io.ktor.http.HttpMethod +import io.ktor.http.takeFrom +import kotlinx.cinterop.cValue +import kotlinx.cinterop.readValue +import kotlinx.cinterop.useContents +import kotlinx.serialization.json.Json +import platform.CoreLocation.CLLocation +import platform.CoreLocation.CLLocationCoordinate2D +import platform.CoreLocation.CLLocationCoordinate2DMake +import platform.CoreLocation.CLLocationManager +import platform.MapKit.MKCoordinateRegionMakeWithDistance +import platform.MapKit.MKLocalSearch +import platform.MapKit.MKLocalSearchRequest +import platform.MapKit.MKMapItem +import platform.UIKit.UIEdgeInsetsZero +import platform.darwin.NSObject +import kotlin.coroutines.resume +import kotlin.coroutines.resumeWithException +import kotlin.coroutines.suspendCoroutine +import kotlin.native.ref.WeakReference + +@Suppress("TooManyFunctions") +actual class GoogleMapController( + mapView: GMSMapView, + private val geoApiKey: String +) : MapController { + private val httpClient = HttpClient() + private val json = Json { + ignoreUnknownKeys = true + } + + private val geoCoder = GMSGeocoder() + private val locationManager = CLLocationManager() + private val delegate = MapDelegate(this) + private val weakMapView = WeakReference(mapView) + + actual var onCameraScrollStateChanged: ((scrolling: Boolean, isUserGesture: Boolean) -> Unit)? = + null + + init { + weakMapView.get()?.delegate = delegate + } + + override suspend fun getAddressByLatLng(latitude: Double, longitude: Double): String? { + val gmsAddress: GMSAddress = suspendCoroutine { continuation -> + val coords = cValue { + this.latitude = latitude + this.longitude = longitude + } + geoCoder.reverseGeocodeCoordinate(coords) { response, error -> + if (error != null) { + continuation.resumeWithException(error.asThrowable()) + return@reverseGeocodeCoordinate + } + + @Suppress("UNCHECKED_CAST") + val addresses = response?.firstResult()?.lines as? List + val firstAddress = addresses?.firstOrNull() + + if (firstAddress == null) { + continuation.resumeWithException(IllegalStateException("empty results")) + return@reverseGeocodeCoordinate + } + + continuation.resume(firstAddress) + } + } + + return gmsAddress.toString() + } + + override suspend fun getSimilarNearAddresses( + text: String?, + maxResults: Int, + maxRadius: Int + ): List { + val location = getCurrentLocation() + + return suspendCoroutine { continuation -> + val request = MKLocalSearchRequest() + request.naturalLanguageQuery = text + + request.region = MKCoordinateRegionMakeWithDistance( + centerCoordinate = CLLocationCoordinate2DMake( + latitude = location.latitude, + longitude = location.longitude + ), + latitudinalMeters = maxRadius.toDouble(), + longitudinalMeters = maxRadius.toDouble() + ) + + val search = MKLocalSearch(request = request) + + search.startWithCompletionHandler( + mainContinuation { response, error -> + if (error != null) { + continuation.resumeWithException(Throwable(error.localizedDescription)) + return@mainContinuation + } + + var addresses = listOf() + val items = response?.mapItems ?: listOf() + + for (item in items) { + val mkItem = item as? MKMapItem + var fullAddress = mkItem?.placemark?.name ?: "" + + val thoroughfare = mkItem?.placemark?.thoroughfare + if (thoroughfare != null && !fullAddress.contains(thoroughfare)) { + fullAddress = fullAddress.plus(", $thoroughfare") + } + + val subThoroughfare = mkItem?.placemark?.subThoroughfare + if (subThoroughfare != null && !fullAddress.contains(subThoroughfare)) { + fullAddress = fullAddress.plus(", $subThoroughfare") + } + + mkItem?.placemark?.coordinate?.useContents { + + val address = MapAddress( + address = fullAddress, + city = mkItem.placemark.locality, + latLng = LatLng( + latitude = this.latitude, + longitude = this.longitude + ), + distance = maxRadius.toDouble() + ) + addresses = addresses.plus(address) + } + } + continuation.resume(addresses.take(maxResults)) + } + ) + } + } + + private fun getCurrentLocation(): LatLng { + val location: CLLocation = weakMapView.get()?.myLocation + ?: locationManager.location + ?: throw IllegalStateException("can't get location") + + return location.coordinate.toLatLng() + } + + override suspend fun addMarker( + image: ImageResource, + latLng: LatLng, + rotation: Float, + onClick: (() -> Unit)? + ): Marker { + val marker = GMSMarker.markerWithPosition(position = latLng.toCoord2D()).also { + it.icon = image.toUIImage() + it.rotation = rotation.toDouble() + it.map = weakMapView.get() + it.tappable = true + it.userData = onClick + } + return GoogleMarker(marker) + } + + override suspend fun buildRoute( + points: List, + lineColor: Color, + markersImage: ImageResource? + ): MapElement { + val builder = HttpRequestBuilder() + builder.method = HttpMethod.Get + + val origin = points.first() + val destination = points.last() + + var waypoints = "" + + if (points.count() > 2) { + waypoints = "&waypoints=" + for (point in points) { + if (point != origin && point != destination) { + waypoints = waypoints.plus("via:${point.latitude},${point.longitude}") + } + } + } + + builder.url { + val originStr = "origin=${origin.latitude},${origin.longitude}" + val destinationStr = "destination=${destination.latitude},${destination.longitude}" + val key = "key=$geoApiKey" + takeFrom("https://maps.googleapis.com/maps/api/directions/json?$originStr&$destinationStr&$key$waypoints") + } + + try { + val result: String = httpClient.request(builder) + return buildRoute(result, lineColor, markersImage) + } catch (pipeline: ReceivePipelineException) { + throw pipeline.cause + } + } + + override suspend fun drawPolygon( + pointList: List, + backgroundColor: Color, + lineColor: Color, + backgroundOpacity: Float, + lineWidth: Float, + lineOpacity: Float, + lineType: LineType + ): MapElement { + val polygonPath = GMSMutablePath().apply { + pointList.forEach { addCoordinate(it.toCoord2D()) } + } + val polygon = GMSPolygon().apply { + path = polygonPath + fillColor = colorWithOpacity(backgroundColor, backgroundOpacity).toUIColor() + strokeColor = colorWithOpacity(lineColor, lineOpacity).toUIColor() + strokeWidth = lineWidth.toDouble() + tappable = false + map = weakMapView.get() + } + if (lineType != LineType.SOLID) { + println("WARNING: GMSPolygon not support line type $lineType") + } + return GooglePolygon(polygon) + } + + private fun buildRoute( + from: String, + lineColor: Color, + markersImage: ImageResource? + ): MapElement { + val direction = json.decodeFromString(GDirection.serializer(), from) + + val route = + direction.routes.firstOrNull() ?: throw IllegalArgumentException("routes not found") + + val path = GMSPath.pathFromEncodedPath(route.overviewPolyline.points) + val routeLine = GMSPolyline.polylineWithPath(path) + + routeLine.strokeColor = lineColor.toUIColor() + routeLine.strokeWidth = ROUTE_STROKE_WIDTH + routeLine.map = weakMapView.get() + + val startMarker = route.legs.firstOrNull()?.startLocation + ?.takeIf { markersImage != null } + ?.let { + GMSMarker.markerWithPosition(it.coord2D()).apply { + icon = markersImage!!.toUIImage() + map = weakMapView.get() + } + } + + val endMarker = route.legs.lastOrNull()?.endLocation + ?.takeIf { markersImage != null } + ?.let { + GMSMarker.markerWithPosition(it.coord2D()).apply { + icon = markersImage!!.toUIImage() + map = weakMapView.get() + } + } + + val firstLeg = route.legs.firstOrNull() + val waypoints = firstLeg?.viaWaypoint + ?.takeIf { markersImage != null } + ?.let { points -> + points.map { + val step = firstLeg.steps[it.stepIndex] + GMSMarker.markerWithPosition(step.endLocation.coord2D()).apply { + icon = markersImage!!.toUIImage() + map = weakMapView.get() + } + } + } + + if (path != null) { + val position = weakMapView.get()?.cameraForBounds( + bounds = GMSCoordinateBounds.create(path = path), + insets = UIEdgeInsetsZero.readValue() + ) + + if (position != null) { + weakMapView.get()?.animateToCameraPosition(position) + } + } + + return GoogleRoute( + routeLine = routeLine, + startMarker = startMarker, + endMarker = endMarker, + wayPointsMarkers = waypoints + ) + } + + override suspend fun getMapCenterLatLng(): LatLng { + return weakMapView.get()?.camera?.target?.toLatLng() ?: LatLng( + latitude = 0.0, + longitude = 0.0 + ) + } + + override fun showLocation(latLng: LatLng, zoom: Float, animation: Boolean) { + val position = GMSCameraPosition( + latitude = latLng.latitude, + longitude = latLng.longitude, + zoom = zoom + ) + if (animation) { + weakMapView.get()?.animateToCameraPosition(position) + } else { + weakMapView.get()?.setCamera(position) + } + } + + override fun showMyLocation(zoom: Float) { + val location = getCurrentLocation() + val position = GMSCameraPosition( + latitude = location.latitude, + longitude = location.longitude, + zoom = zoom + ) + weakMapView.get()?.animateToCameraPosition(position) + } + + override suspend fun getCurrentZoom(): Float { + return weakMapView.get()?.camera?.zoom ?: 0f + } + + override suspend fun setCurrentZoom(zoom: Float) { + weakMapView.get()?.animateToZoom(zoom) + } + + override suspend fun getZoomConfig(): ZoomConfig { + return ZoomConfig( + min = weakMapView.get()?.minZoom, + max = weakMapView.get()?.maxZoom + ) + } + + override suspend fun setZoomConfig(config: ZoomConfig) { + weakMapView.get()?.setMinZoom( + minZoom = config.min ?: kGMSMinZoomLevel, + maxZoom = config.max ?: kGMSMaxZoomLevel + ) + } + + actual suspend fun readUiSettings(): UiSettings { + val settings = weakMapView.get()?.settings + return UiSettings( + compassEnabled = settings?.compassButton ?: false, + myLocationButtonEnabled = settings?.myLocationButton ?: false, + indoorLevelPickerEnabled = settings?.indoorPicker ?: false, + scrollGesturesEnabled = settings?.scrollGestures ?: false, + zoomGesturesEnabled = settings?.zoomGestures ?: false, + tiltGesturesEnabled = settings?.tiltGestures ?: false, + rotateGesturesEnabled = settings?.rotateGestures ?: false, + scrollGesturesDuringRotateOrZoomEnabled = settings?.allowScrollGesturesDuringRotateOrZoom + ?: false + ) + } + + actual fun writeUiSettings(settings: UiSettings) { + weakMapView.get()?.settings?.let { + it.compassButton = settings.compassEnabled + it.myLocationButton = settings.myLocationButtonEnabled + it.indoorPicker = settings.indoorLevelPickerEnabled + it.scrollGestures = settings.scrollGesturesEnabled + it.zoomGestures = settings.zoomGesturesEnabled + it.tiltGestures = settings.tiltGesturesEnabled + it.rotateGestures = settings.rotateGesturesEnabled + it.allowScrollGesturesDuringRotateOrZoom = + settings.scrollGesturesDuringRotateOrZoomEnabled + } + weakMapView.get()?.myLocationEnabled = + settings.myLocationButtonEnabled || settings.myLocationEnabled + } + + @Suppress("PARAMETER_NAME_CHANGED_ON_OVERRIDE") + private class MapDelegate( + mapController: GoogleMapController + ) : NSObject(), GMSMapViewDelegateProtocol { + private val mapController = WeakReference(mapController) + + @Suppress("RETURN_TYPE_MISMATCH_ON_OVERRIDE") + override fun mapView(mapView: GMSMapView, didTapMarker: GMSMarker): Boolean { + val marker: GMSMarker = didTapMarker + + @Suppress("UNCHECKED_CAST") + (marker.userData as? (() -> Unit))?.invoke() + + return false // not show any info box + } + + override fun mapView(mapView: GMSMapView, willMove: Boolean) { + mapController.get()?.onCameraScrollStateChanged?.invoke(true, willMove) + } + + override fun mapView(mapView: GMSMapView, idleAtCameraPosition: GMSCameraPosition) { + mapController.get()?.onCameraScrollStateChanged?.invoke(false, false) + } + } + + private companion object { + const val ROUTE_STROKE_WIDTH = 3.0 + } +} diff --git a/maps-google/src/iosSimulatorArm64Main/kotlin/dev/icerock/moko/maps/google/GoogleMarker.kt b/maps-google/src/iosSimulatorArm64Main/kotlin/dev/icerock/moko/maps/google/GoogleMarker.kt new file mode 100644 index 0000000..5220598 --- /dev/null +++ b/maps-google/src/iosSimulatorArm64Main/kotlin/dev/icerock/moko/maps/google/GoogleMarker.kt @@ -0,0 +1,43 @@ +/* + * Copyright 2019 IceRock MAG Inc. Use of this source code is governed by the Apache 2.0 license. + */ + +package dev.icerock.moko.maps.google + +import cocoapods.GoogleMaps.GMSMarker +import dev.icerock.moko.geo.LatLng +import dev.icerock.moko.maps.Marker +import platform.QuartzCore.CATransaction +import kotlin.time.Duration +import kotlin.time.ExperimentalTime + +actual class GoogleMarker( + private val gmsMarker: GMSMarker +) : Marker { + override var position: LatLng + get() = gmsMarker.position.toLatLng() + set(value) { + gmsMarker.position = value.toCoord2D() + } + + override var rotation: Float + get() = gmsMarker.rotation.toFloat() + set(value) { + gmsMarker.rotation = value.toDouble() + } + + override fun delete() { + gmsMarker.map = null + } + + @OptIn(ExperimentalTime::class) + override fun move(position: LatLng, rotation: Float, duration: Duration) { + CATransaction.begin() + CATransaction.setAnimationDuration(duration.inSeconds) + + gmsMarker.position = position.toCoord2D() + gmsMarker.rotation = rotation.toDouble() + + CATransaction.commit() + } +} diff --git a/maps-google/src/iosSimulatorArm64Main/kotlin/dev/icerock/moko/maps/google/GooglePolygon.kt b/maps-google/src/iosSimulatorArm64Main/kotlin/dev/icerock/moko/maps/google/GooglePolygon.kt new file mode 100644 index 0000000..278c9b6 --- /dev/null +++ b/maps-google/src/iosSimulatorArm64Main/kotlin/dev/icerock/moko/maps/google/GooglePolygon.kt @@ -0,0 +1,14 @@ +/* + * Copyright 2020 IceRock MAG Inc. Use of this source code is governed by the Apache 2.0 license. + */ + +package dev.icerock.moko.maps.google + +import cocoapods.GoogleMaps.GMSPolygon +import dev.icerock.moko.maps.MapElement + +data class GooglePolygon(val polygon: GMSPolygon) : MapElement { + override fun delete() { + polygon.map = null + } +} diff --git a/maps-google/src/iosSimulatorArm64Main/kotlin/dev/icerock/moko/maps/google/GoogleRoute.kt b/maps-google/src/iosSimulatorArm64Main/kotlin/dev/icerock/moko/maps/google/GoogleRoute.kt new file mode 100644 index 0000000..75e5c6d --- /dev/null +++ b/maps-google/src/iosSimulatorArm64Main/kotlin/dev/icerock/moko/maps/google/GoogleRoute.kt @@ -0,0 +1,23 @@ +/* + * Copyright 2019 IceRock MAG Inc. Use of this source code is governed by the Apache 2.0 license. + */ + +package dev.icerock.moko.maps.google + +import cocoapods.GoogleMaps.GMSMarker +import cocoapods.GoogleMaps.GMSPolyline +import dev.icerock.moko.maps.MapElement + +actual class GoogleRoute( + val routeLine: GMSPolyline, + val startMarker: GMSMarker?, + val endMarker: GMSMarker?, + val wayPointsMarkers: List? +) : MapElement { + override fun delete() { + wayPointsMarkers?.forEach { it.map = null } + routeLine.map = null + startMarker?.map = null + endMarker?.map = null + } +} diff --git a/maps-mapbox/build.gradle.kts b/maps-mapbox/build.gradle.kts index 92242b6..5f8027a 100644 --- a/maps-mapbox/build.gradle.kts +++ b/maps-mapbox/build.gradle.kts @@ -3,9 +3,11 @@ */ plugins { - id("multiplatform-library-convention") id("dev.icerock.mobile.multiplatform.android-manifest") - id("publication-convention") + id("dev.icerock.moko.gradle.multiplatform.mobile") + id("dev.icerock.moko.gradle.publication") + id("dev.icerock.moko.gradle.stub.javadoc") + id("dev.icerock.moko.gradle.detekt") id("kotlin-parcelize") id("dev.icerock.mobile.multiplatform.cocoapods") } diff --git a/maps/build.gradle.kts b/maps/build.gradle.kts index 3e12ebf..77ca2bd 100644 --- a/maps/build.gradle.kts +++ b/maps/build.gradle.kts @@ -3,9 +3,11 @@ */ plugins { - id("multiplatform-library-convention") id("dev.icerock.mobile.multiplatform.android-manifest") - id("publication-convention") + id("dev.icerock.moko.gradle.multiplatform.mobile") + id("dev.icerock.moko.gradle.publication") + id("dev.icerock.moko.gradle.stub.javadoc") + id("dev.icerock.moko.gradle.detekt") id("kotlin-kapt") id("kotlin-parcelize") } diff --git a/sample/android-app/build.gradle.kts b/sample/android-app/build.gradle.kts index 4241244..f648223 100644 --- a/sample/android-app/build.gradle.kts +++ b/sample/android-app/build.gradle.kts @@ -1,5 +1,6 @@ plugins { - id("android-app-convention") + id("dev.icerock.moko.gradle.android.application") + id("dev.icerock.moko.gradle.detekt") id("kotlin-android") id("kotlin-kapt") } @@ -15,12 +16,12 @@ android { multiDexEnabled = true - val googleMapsApiKey: String = (System.getenv("GOOGLE_MAPS_API_KEY") ?: extra["googleMaps.apiKey"] as? String).orEmpty() - val mapboxPublicToken: String = (System.getenv("MAPBOX_PUBLIC_TOKEN") ?: extra["mapbox.publicToken"] as? String).orEmpty() +// val googleMapsApiKey: String = (System.getenv("GOOGLE_MAPS_API_KEY") ?: extra["googleMaps.apiKey"] as? String).orEmpty() +// val mapboxPublicToken: String = (System.getenv("MAPBOX_PUBLIC_TOKEN") ?: extra["mapbox.publicToken"] as? String).orEmpty() - manifestPlaceholders["googleMapsApiKey"] = googleMapsApiKey - buildConfigField("String", "GOOGLE_MAPS_API_KEY", "\"$googleMapsApiKey\"") - buildConfigField("String", "MAPBOX_PUBLIC_TOKEN", "\"$mapboxPublicToken\"") +// manifestPlaceholders["googleMapsApiKey"] = googleMapsApiKey +// buildConfigField("String", "GOOGLE_MAPS_API_KEY", "\"$googleMapsApiKey\"") +// buildConfigField("String", "MAPBOX_PUBLIC_TOKEN", "\"$mapboxPublicToken\"") } } diff --git a/sample/mpp-library/build.gradle.kts b/sample/mpp-library/build.gradle.kts index 2268b59..e9c4288 100644 --- a/sample/mpp-library/build.gradle.kts +++ b/sample/mpp-library/build.gradle.kts @@ -1,19 +1,15 @@ plugins { id("com.android.library") - id("android-base-convention") - id("detekt-convention") + id("dev.icerock.moko.gradle.android.base") id("org.jetbrains.kotlin.multiplatform") + id("dev.icerock.mobile.multiplatform.targets") + id("dev.icerock.moko.gradle.detekt") id("dev.icerock.mobile.multiplatform.android-manifest") id("dev.icerock.mobile.multiplatform.ios-framework") id("dev.icerock.mobile.multiplatform-resources") id("dev.icerock.mobile.multiplatform.cocoapods") } -kotlin{ - android() - ios() -} - dependencies { commonMainImplementation(libs.coroutines) commonMainImplementation(libs.mokoResources) @@ -23,7 +19,7 @@ dependencies { commonMainApi(projects.maps) commonMainApi(projects.mapsGoogle) commonMainApi(projects.mapsMapbox) - "androidMainImplementation"(libs.lifecycle) + androidMainImplementation(libs.lifecycle) } multiplatformResources { diff --git a/settings.gradle.kts b/settings.gradle.kts index 697b2c0..4c66b9c 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -8,6 +8,7 @@ enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS") dependencyResolutionManagement { repositories { mavenCentral() + mavenLocal() google() maven { @@ -33,8 +34,6 @@ dependencyResolutionManagement { } } -includeBuild("maps-build-logic") - include(":maps") include(":maps-google") include(":maps-mapbox") From d221caf82d23b4fbb1ddfb5ebe36dcef3406629b Mon Sep 17 00:00:00 2001 From: Aleksey Mikhailov Date: Fri, 1 Apr 2022 21:43:01 +0700 Subject: [PATCH 2/4] review fixes --- README.md | 6 +- build.gradle.kts | 12 +- gradle/libs.versions.toml | 6 +- maps-google/build.gradle.kts | 15 +- maps-google/src/iosSimulatorArm64Main | 1 + .../moko/maps/google/GoogleMapController.kt | 439 ------------------ .../icerock/moko/maps/google/GoogleMarker.kt | 43 -- .../icerock/moko/maps/google/GooglePolygon.kt | 14 - .../icerock/moko/maps/google/GoogleRoute.kt | 23 - .../moko/maps/google/GoogleMapController.kt | 1 + maps-mapbox/build.gradle.kts | 15 +- .../moko/maps/mapbox/MapboxController.kt | 9 +- .../icerock/moko/maps/mapbox/MapboxMarker.kt | 2 +- maps-mapbox/src/iosSimulatorArm64Main | 1 + .../moko/maps/mapbox/MapboxController.kt | 6 +- .../icerock/moko/maps/mapbox/MapboxMarker.kt | 4 +- maps/build.gradle.kts | 1 - .../kotlin/dev/icerock/moko/maps/Marker.kt | 2 - sample/android-app/build.gradle.kts | 14 +- .../android-app/src/main/AndroidManifest.xml | 4 +- .../com/icerockdev/app/GoogleMapsActivity.kt | 1 - .../java/com/icerockdev/app/MainActivity.kt | 2 +- .../java/com/icerockdev/app/MapboxActivity.kt | 1 - sample/mpp-library/build.gradle.kts | 19 +- settings.gradle.kts | 7 - 25 files changed, 66 insertions(+), 582 deletions(-) create mode 120000 maps-google/src/iosSimulatorArm64Main delete mode 100644 maps-google/src/iosSimulatorArm64Main/kotlin/dev/icerock/moko/maps/google/GoogleMapController.kt delete mode 100644 maps-google/src/iosSimulatorArm64Main/kotlin/dev/icerock/moko/maps/google/GoogleMarker.kt delete mode 100644 maps-google/src/iosSimulatorArm64Main/kotlin/dev/icerock/moko/maps/google/GooglePolygon.kt delete mode 100644 maps-google/src/iosSimulatorArm64Main/kotlin/dev/icerock/moko/maps/google/GoogleRoute.kt create mode 120000 maps-mapbox/src/iosSimulatorArm64Main diff --git a/README.md b/README.md index 0b1aa90..fd726a2 100755 --- a/README.md +++ b/README.md @@ -38,9 +38,9 @@ allprojects { project build.gradle ```groovy dependencies { - commonMainApi("dev.icerock.moko:maps:0.6.0") - commonMainApi("dev.icerock.moko:maps-google:0.6.0") - commonMainApi("dev.icerock.moko:maps-mapbox:0.6.0") + commonMainApi("dev.icerock.moko:maps:0.7.0") + commonMainApi("dev.icerock.moko:maps-google:0.7.0") + commonMainApi("dev.icerock.moko:maps-mapbox:0.7.0") } kotlin.targets diff --git a/build.gradle.kts b/build.gradle.kts index 0c957e9..c2bd0b0 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -5,10 +5,7 @@ buildscript { repositories { mavenCentral() - mavenLocal() - google() - gradlePluginPortal() } @@ -24,14 +21,13 @@ buildscript { } } +apply(plugin = "dev.icerock.moko.gradle.publication.nexus") +val mokoVersion = libs.versions.mokoMapsVersion.get() allprojects { - plugins.withId("org.gradle.maven-publish") { - group = "dev.icerock.moko" - version = libs.versions.mokoMapsVersion.get() - } + group = "dev.icerock.moko" + version = mokoVersion } - tasks.register("clean", Delete::class).configure { delete(rootProject.buildDir) } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 46cd001..b0ec946 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -39,15 +39,19 @@ ktorClientOkHttp = { module = "io.ktor:ktor-client-okhttp", version.ref = "ktorC kotlinSerialization = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "kotlinxSerializationVersion" } coroutines = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "coroutinesVersion" } ktorClient = { module = "io.ktor:ktor-client-core", version.ref = "ktorClientVersion" } + mokoResources = { module = "dev.icerock.moko:resources", version.ref = "mokoResourcesVersion" } mokoParcelize = { module = "dev.icerock.moko:parcelize", version.ref = "mokoParcelizeVersion" } mokoGraphics = { module = "dev.icerock.moko:graphics", version.ref = "mokoGraphicsVersion" } -mokoMvvm = { module = "dev.icerock.moko:mvvm-core", version.ref = "mokoMvvmVersion" } +mokoMvvmCore = { module = "dev.icerock.moko:mvvm-core", version.ref = "mokoMvvmVersion" } +mokoMvvmLiveData = { module = "dev.icerock.moko:mvvm-livedata", version.ref = "mokoMvvmVersion" } +mokoMvvmDataBinding = { module = "dev.icerock.moko:mvvm-databinding", version.ref = "mokoMvvmVersion" } mokoGeo = { module = "dev.icerock.moko:geo", version.ref = "mokoGeoVersion" } mokoPermissions = { module = "dev.icerock.moko:permissions", version.ref = "mokoPermissionsVersion" } mokoMaps = { module = "dev.icerock.moko:maps", version.ref = "mokoMapsVersion" } mokoMapsGoogle = { module = "dev.icerock.moko:maps-google", version.ref = "mokoMapsVersion" } mokoMapsMapbox = { module = "dev.icerock.moko:maps-mapbox", version.ref = "mokoMapsVersion" } + kotlinTest = { module = "org.jetbrains.kotlin:kotlin-test-common", version.ref = "kotlinVersion" } ktorClientIos = { module = "io.ktor:ktor-client-ios", version.ref = "ktorClientVersion" } diff --git a/maps-google/build.gradle.kts b/maps-google/build.gradle.kts index d11b91d..f0a4316 100644 --- a/maps-google/build.gradle.kts +++ b/maps-google/build.gradle.kts @@ -3,7 +3,6 @@ */ plugins { - id("dev.icerock.mobile.multiplatform.android-manifest") id("dev.icerock.moko.gradle.multiplatform.mobile") id("dev.icerock.moko.gradle.publication") id("dev.icerock.moko.gradle.stub.javadoc") @@ -23,14 +22,14 @@ dependencies { commonMainApi(libs.mokoGeo) commonMainApi(libs.mokoGraphics) - "androidMainImplementation"(libs.appCompat) - "androidMainImplementation"(libs.lifecycle) - "androidMainApi"(libs.playServicesLocation) - "androidMainApi"(libs.playServicesMaps) - "androidMainImplementation"(libs.googleMapsServices) - "androidMainImplementation"(libs.ktorClientOkHttp) + androidMainImplementation(libs.appCompat) + androidMainImplementation(libs.lifecycle) + androidMainApi(libs.playServicesLocation) + androidMainApi(libs.playServicesMaps) + androidMainImplementation(libs.googleMapsServices) - "iosMainImplementation"(libs.ktorClientIos) + androidMainImplementation(libs.ktorClientOkHttp) + iosMainImplementation(libs.ktorClientIos) } cocoaPods { diff --git a/maps-google/src/iosSimulatorArm64Main b/maps-google/src/iosSimulatorArm64Main new file mode 120000 index 0000000..46be4b8 --- /dev/null +++ b/maps-google/src/iosSimulatorArm64Main @@ -0,0 +1 @@ +iosX64Main \ No newline at end of file diff --git a/maps-google/src/iosSimulatorArm64Main/kotlin/dev/icerock/moko/maps/google/GoogleMapController.kt b/maps-google/src/iosSimulatorArm64Main/kotlin/dev/icerock/moko/maps/google/GoogleMapController.kt deleted file mode 100644 index c0310a5..0000000 --- a/maps-google/src/iosSimulatorArm64Main/kotlin/dev/icerock/moko/maps/google/GoogleMapController.kt +++ /dev/null @@ -1,439 +0,0 @@ -/* - * Copyright 2019 IceRock MAG Inc. Use of this source code is governed by the Apache 2.0 license. - */ - -package dev.icerock.moko.maps.google - -import cocoapods.GoogleMaps.GMSAddress -import cocoapods.GoogleMaps.GMSCameraPosition -import cocoapods.GoogleMaps.GMSCoordinateBounds -import cocoapods.GoogleMaps.GMSGeocoder -import cocoapods.GoogleMaps.GMSMapView -import cocoapods.GoogleMaps.GMSMapViewDelegateProtocol -import cocoapods.GoogleMaps.GMSMarker -import cocoapods.GoogleMaps.GMSMutablePath -import cocoapods.GoogleMaps.GMSPath -import cocoapods.GoogleMaps.GMSPolygon -import cocoapods.GoogleMaps.GMSPolyline -import cocoapods.GoogleMaps.animateToCameraPosition -import cocoapods.GoogleMaps.animateToZoom -import cocoapods.GoogleMaps.create -import cocoapods.GoogleMaps.kGMSMaxZoomLevel -import cocoapods.GoogleMaps.kGMSMinZoomLevel -import dev.icerock.moko.geo.LatLng -import dev.icerock.moko.graphics.Color -import dev.icerock.moko.graphics.toUIColor -import dev.icerock.moko.maps.LineType -import dev.icerock.moko.maps.MapAddress -import dev.icerock.moko.maps.MapController -import dev.icerock.moko.maps.MapElement -import dev.icerock.moko.maps.Marker -import dev.icerock.moko.maps.ZoomConfig -import dev.icerock.moko.resources.ImageResource -import io.ktor.client.HttpClient -import io.ktor.client.call.ReceivePipelineException -import io.ktor.client.request.HttpRequestBuilder -import io.ktor.client.request.request -import io.ktor.http.HttpMethod -import io.ktor.http.takeFrom -import kotlinx.cinterop.cValue -import kotlinx.cinterop.readValue -import kotlinx.cinterop.useContents -import kotlinx.serialization.json.Json -import platform.CoreLocation.CLLocation -import platform.CoreLocation.CLLocationCoordinate2D -import platform.CoreLocation.CLLocationCoordinate2DMake -import platform.CoreLocation.CLLocationManager -import platform.MapKit.MKCoordinateRegionMakeWithDistance -import platform.MapKit.MKLocalSearch -import platform.MapKit.MKLocalSearchRequest -import platform.MapKit.MKMapItem -import platform.UIKit.UIEdgeInsetsZero -import platform.darwin.NSObject -import kotlin.coroutines.resume -import kotlin.coroutines.resumeWithException -import kotlin.coroutines.suspendCoroutine -import kotlin.native.ref.WeakReference - -@Suppress("TooManyFunctions") -actual class GoogleMapController( - mapView: GMSMapView, - private val geoApiKey: String -) : MapController { - private val httpClient = HttpClient() - private val json = Json { - ignoreUnknownKeys = true - } - - private val geoCoder = GMSGeocoder() - private val locationManager = CLLocationManager() - private val delegate = MapDelegate(this) - private val weakMapView = WeakReference(mapView) - - actual var onCameraScrollStateChanged: ((scrolling: Boolean, isUserGesture: Boolean) -> Unit)? = - null - - init { - weakMapView.get()?.delegate = delegate - } - - override suspend fun getAddressByLatLng(latitude: Double, longitude: Double): String? { - val gmsAddress: GMSAddress = suspendCoroutine { continuation -> - val coords = cValue { - this.latitude = latitude - this.longitude = longitude - } - geoCoder.reverseGeocodeCoordinate(coords) { response, error -> - if (error != null) { - continuation.resumeWithException(error.asThrowable()) - return@reverseGeocodeCoordinate - } - - @Suppress("UNCHECKED_CAST") - val addresses = response?.firstResult()?.lines as? List - val firstAddress = addresses?.firstOrNull() - - if (firstAddress == null) { - continuation.resumeWithException(IllegalStateException("empty results")) - return@reverseGeocodeCoordinate - } - - continuation.resume(firstAddress) - } - } - - return gmsAddress.toString() - } - - override suspend fun getSimilarNearAddresses( - text: String?, - maxResults: Int, - maxRadius: Int - ): List { - val location = getCurrentLocation() - - return suspendCoroutine { continuation -> - val request = MKLocalSearchRequest() - request.naturalLanguageQuery = text - - request.region = MKCoordinateRegionMakeWithDistance( - centerCoordinate = CLLocationCoordinate2DMake( - latitude = location.latitude, - longitude = location.longitude - ), - latitudinalMeters = maxRadius.toDouble(), - longitudinalMeters = maxRadius.toDouble() - ) - - val search = MKLocalSearch(request = request) - - search.startWithCompletionHandler( - mainContinuation { response, error -> - if (error != null) { - continuation.resumeWithException(Throwable(error.localizedDescription)) - return@mainContinuation - } - - var addresses = listOf() - val items = response?.mapItems ?: listOf() - - for (item in items) { - val mkItem = item as? MKMapItem - var fullAddress = mkItem?.placemark?.name ?: "" - - val thoroughfare = mkItem?.placemark?.thoroughfare - if (thoroughfare != null && !fullAddress.contains(thoroughfare)) { - fullAddress = fullAddress.plus(", $thoroughfare") - } - - val subThoroughfare = mkItem?.placemark?.subThoroughfare - if (subThoroughfare != null && !fullAddress.contains(subThoroughfare)) { - fullAddress = fullAddress.plus(", $subThoroughfare") - } - - mkItem?.placemark?.coordinate?.useContents { - - val address = MapAddress( - address = fullAddress, - city = mkItem.placemark.locality, - latLng = LatLng( - latitude = this.latitude, - longitude = this.longitude - ), - distance = maxRadius.toDouble() - ) - addresses = addresses.plus(address) - } - } - continuation.resume(addresses.take(maxResults)) - } - ) - } - } - - private fun getCurrentLocation(): LatLng { - val location: CLLocation = weakMapView.get()?.myLocation - ?: locationManager.location - ?: throw IllegalStateException("can't get location") - - return location.coordinate.toLatLng() - } - - override suspend fun addMarker( - image: ImageResource, - latLng: LatLng, - rotation: Float, - onClick: (() -> Unit)? - ): Marker { - val marker = GMSMarker.markerWithPosition(position = latLng.toCoord2D()).also { - it.icon = image.toUIImage() - it.rotation = rotation.toDouble() - it.map = weakMapView.get() - it.tappable = true - it.userData = onClick - } - return GoogleMarker(marker) - } - - override suspend fun buildRoute( - points: List, - lineColor: Color, - markersImage: ImageResource? - ): MapElement { - val builder = HttpRequestBuilder() - builder.method = HttpMethod.Get - - val origin = points.first() - val destination = points.last() - - var waypoints = "" - - if (points.count() > 2) { - waypoints = "&waypoints=" - for (point in points) { - if (point != origin && point != destination) { - waypoints = waypoints.plus("via:${point.latitude},${point.longitude}") - } - } - } - - builder.url { - val originStr = "origin=${origin.latitude},${origin.longitude}" - val destinationStr = "destination=${destination.latitude},${destination.longitude}" - val key = "key=$geoApiKey" - takeFrom("https://maps.googleapis.com/maps/api/directions/json?$originStr&$destinationStr&$key$waypoints") - } - - try { - val result: String = httpClient.request(builder) - return buildRoute(result, lineColor, markersImage) - } catch (pipeline: ReceivePipelineException) { - throw pipeline.cause - } - } - - override suspend fun drawPolygon( - pointList: List, - backgroundColor: Color, - lineColor: Color, - backgroundOpacity: Float, - lineWidth: Float, - lineOpacity: Float, - lineType: LineType - ): MapElement { - val polygonPath = GMSMutablePath().apply { - pointList.forEach { addCoordinate(it.toCoord2D()) } - } - val polygon = GMSPolygon().apply { - path = polygonPath - fillColor = colorWithOpacity(backgroundColor, backgroundOpacity).toUIColor() - strokeColor = colorWithOpacity(lineColor, lineOpacity).toUIColor() - strokeWidth = lineWidth.toDouble() - tappable = false - map = weakMapView.get() - } - if (lineType != LineType.SOLID) { - println("WARNING: GMSPolygon not support line type $lineType") - } - return GooglePolygon(polygon) - } - - private fun buildRoute( - from: String, - lineColor: Color, - markersImage: ImageResource? - ): MapElement { - val direction = json.decodeFromString(GDirection.serializer(), from) - - val route = - direction.routes.firstOrNull() ?: throw IllegalArgumentException("routes not found") - - val path = GMSPath.pathFromEncodedPath(route.overviewPolyline.points) - val routeLine = GMSPolyline.polylineWithPath(path) - - routeLine.strokeColor = lineColor.toUIColor() - routeLine.strokeWidth = ROUTE_STROKE_WIDTH - routeLine.map = weakMapView.get() - - val startMarker = route.legs.firstOrNull()?.startLocation - ?.takeIf { markersImage != null } - ?.let { - GMSMarker.markerWithPosition(it.coord2D()).apply { - icon = markersImage!!.toUIImage() - map = weakMapView.get() - } - } - - val endMarker = route.legs.lastOrNull()?.endLocation - ?.takeIf { markersImage != null } - ?.let { - GMSMarker.markerWithPosition(it.coord2D()).apply { - icon = markersImage!!.toUIImage() - map = weakMapView.get() - } - } - - val firstLeg = route.legs.firstOrNull() - val waypoints = firstLeg?.viaWaypoint - ?.takeIf { markersImage != null } - ?.let { points -> - points.map { - val step = firstLeg.steps[it.stepIndex] - GMSMarker.markerWithPosition(step.endLocation.coord2D()).apply { - icon = markersImage!!.toUIImage() - map = weakMapView.get() - } - } - } - - if (path != null) { - val position = weakMapView.get()?.cameraForBounds( - bounds = GMSCoordinateBounds.create(path = path), - insets = UIEdgeInsetsZero.readValue() - ) - - if (position != null) { - weakMapView.get()?.animateToCameraPosition(position) - } - } - - return GoogleRoute( - routeLine = routeLine, - startMarker = startMarker, - endMarker = endMarker, - wayPointsMarkers = waypoints - ) - } - - override suspend fun getMapCenterLatLng(): LatLng { - return weakMapView.get()?.camera?.target?.toLatLng() ?: LatLng( - latitude = 0.0, - longitude = 0.0 - ) - } - - override fun showLocation(latLng: LatLng, zoom: Float, animation: Boolean) { - val position = GMSCameraPosition( - latitude = latLng.latitude, - longitude = latLng.longitude, - zoom = zoom - ) - if (animation) { - weakMapView.get()?.animateToCameraPosition(position) - } else { - weakMapView.get()?.setCamera(position) - } - } - - override fun showMyLocation(zoom: Float) { - val location = getCurrentLocation() - val position = GMSCameraPosition( - latitude = location.latitude, - longitude = location.longitude, - zoom = zoom - ) - weakMapView.get()?.animateToCameraPosition(position) - } - - override suspend fun getCurrentZoom(): Float { - return weakMapView.get()?.camera?.zoom ?: 0f - } - - override suspend fun setCurrentZoom(zoom: Float) { - weakMapView.get()?.animateToZoom(zoom) - } - - override suspend fun getZoomConfig(): ZoomConfig { - return ZoomConfig( - min = weakMapView.get()?.minZoom, - max = weakMapView.get()?.maxZoom - ) - } - - override suspend fun setZoomConfig(config: ZoomConfig) { - weakMapView.get()?.setMinZoom( - minZoom = config.min ?: kGMSMinZoomLevel, - maxZoom = config.max ?: kGMSMaxZoomLevel - ) - } - - actual suspend fun readUiSettings(): UiSettings { - val settings = weakMapView.get()?.settings - return UiSettings( - compassEnabled = settings?.compassButton ?: false, - myLocationButtonEnabled = settings?.myLocationButton ?: false, - indoorLevelPickerEnabled = settings?.indoorPicker ?: false, - scrollGesturesEnabled = settings?.scrollGestures ?: false, - zoomGesturesEnabled = settings?.zoomGestures ?: false, - tiltGesturesEnabled = settings?.tiltGestures ?: false, - rotateGesturesEnabled = settings?.rotateGestures ?: false, - scrollGesturesDuringRotateOrZoomEnabled = settings?.allowScrollGesturesDuringRotateOrZoom - ?: false - ) - } - - actual fun writeUiSettings(settings: UiSettings) { - weakMapView.get()?.settings?.let { - it.compassButton = settings.compassEnabled - it.myLocationButton = settings.myLocationButtonEnabled - it.indoorPicker = settings.indoorLevelPickerEnabled - it.scrollGestures = settings.scrollGesturesEnabled - it.zoomGestures = settings.zoomGesturesEnabled - it.tiltGestures = settings.tiltGesturesEnabled - it.rotateGestures = settings.rotateGesturesEnabled - it.allowScrollGesturesDuringRotateOrZoom = - settings.scrollGesturesDuringRotateOrZoomEnabled - } - weakMapView.get()?.myLocationEnabled = - settings.myLocationButtonEnabled || settings.myLocationEnabled - } - - @Suppress("PARAMETER_NAME_CHANGED_ON_OVERRIDE") - private class MapDelegate( - mapController: GoogleMapController - ) : NSObject(), GMSMapViewDelegateProtocol { - private val mapController = WeakReference(mapController) - - @Suppress("RETURN_TYPE_MISMATCH_ON_OVERRIDE") - override fun mapView(mapView: GMSMapView, didTapMarker: GMSMarker): Boolean { - val marker: GMSMarker = didTapMarker - - @Suppress("UNCHECKED_CAST") - (marker.userData as? (() -> Unit))?.invoke() - - return false // not show any info box - } - - override fun mapView(mapView: GMSMapView, willMove: Boolean) { - mapController.get()?.onCameraScrollStateChanged?.invoke(true, willMove) - } - - override fun mapView(mapView: GMSMapView, idleAtCameraPosition: GMSCameraPosition) { - mapController.get()?.onCameraScrollStateChanged?.invoke(false, false) - } - } - - private companion object { - const val ROUTE_STROKE_WIDTH = 3.0 - } -} diff --git a/maps-google/src/iosSimulatorArm64Main/kotlin/dev/icerock/moko/maps/google/GoogleMarker.kt b/maps-google/src/iosSimulatorArm64Main/kotlin/dev/icerock/moko/maps/google/GoogleMarker.kt deleted file mode 100644 index 5220598..0000000 --- a/maps-google/src/iosSimulatorArm64Main/kotlin/dev/icerock/moko/maps/google/GoogleMarker.kt +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright 2019 IceRock MAG Inc. Use of this source code is governed by the Apache 2.0 license. - */ - -package dev.icerock.moko.maps.google - -import cocoapods.GoogleMaps.GMSMarker -import dev.icerock.moko.geo.LatLng -import dev.icerock.moko.maps.Marker -import platform.QuartzCore.CATransaction -import kotlin.time.Duration -import kotlin.time.ExperimentalTime - -actual class GoogleMarker( - private val gmsMarker: GMSMarker -) : Marker { - override var position: LatLng - get() = gmsMarker.position.toLatLng() - set(value) { - gmsMarker.position = value.toCoord2D() - } - - override var rotation: Float - get() = gmsMarker.rotation.toFloat() - set(value) { - gmsMarker.rotation = value.toDouble() - } - - override fun delete() { - gmsMarker.map = null - } - - @OptIn(ExperimentalTime::class) - override fun move(position: LatLng, rotation: Float, duration: Duration) { - CATransaction.begin() - CATransaction.setAnimationDuration(duration.inSeconds) - - gmsMarker.position = position.toCoord2D() - gmsMarker.rotation = rotation.toDouble() - - CATransaction.commit() - } -} diff --git a/maps-google/src/iosSimulatorArm64Main/kotlin/dev/icerock/moko/maps/google/GooglePolygon.kt b/maps-google/src/iosSimulatorArm64Main/kotlin/dev/icerock/moko/maps/google/GooglePolygon.kt deleted file mode 100644 index 278c9b6..0000000 --- a/maps-google/src/iosSimulatorArm64Main/kotlin/dev/icerock/moko/maps/google/GooglePolygon.kt +++ /dev/null @@ -1,14 +0,0 @@ -/* - * Copyright 2020 IceRock MAG Inc. Use of this source code is governed by the Apache 2.0 license. - */ - -package dev.icerock.moko.maps.google - -import cocoapods.GoogleMaps.GMSPolygon -import dev.icerock.moko.maps.MapElement - -data class GooglePolygon(val polygon: GMSPolygon) : MapElement { - override fun delete() { - polygon.map = null - } -} diff --git a/maps-google/src/iosSimulatorArm64Main/kotlin/dev/icerock/moko/maps/google/GoogleRoute.kt b/maps-google/src/iosSimulatorArm64Main/kotlin/dev/icerock/moko/maps/google/GoogleRoute.kt deleted file mode 100644 index 75e5c6d..0000000 --- a/maps-google/src/iosSimulatorArm64Main/kotlin/dev/icerock/moko/maps/google/GoogleRoute.kt +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright 2019 IceRock MAG Inc. Use of this source code is governed by the Apache 2.0 license. - */ - -package dev.icerock.moko.maps.google - -import cocoapods.GoogleMaps.GMSMarker -import cocoapods.GoogleMaps.GMSPolyline -import dev.icerock.moko.maps.MapElement - -actual class GoogleRoute( - val routeLine: GMSPolyline, - val startMarker: GMSMarker?, - val endMarker: GMSMarker?, - val wayPointsMarkers: List? -) : MapElement { - override fun delete() { - wayPointsMarkers?.forEach { it.map = null } - routeLine.map = null - startMarker?.map = null - endMarker?.map = null - } -} diff --git a/maps-google/src/iosX64Main/kotlin/dev/icerock/moko/maps/google/GoogleMapController.kt b/maps-google/src/iosX64Main/kotlin/dev/icerock/moko/maps/google/GoogleMapController.kt index c0310a5..ba98be3 100644 --- a/maps-google/src/iosX64Main/kotlin/dev/icerock/moko/maps/google/GoogleMapController.kt +++ b/maps-google/src/iosX64Main/kotlin/dev/icerock/moko/maps/google/GoogleMapController.kt @@ -224,6 +224,7 @@ actual class GoogleMapController( takeFrom("https://maps.googleapis.com/maps/api/directions/json?$originStr&$destinationStr&$key$waypoints") } + @Suppress("SwallowedException") try { val result: String = httpClient.request(builder) return buildRoute(result, lineColor, markersImage) diff --git a/maps-mapbox/build.gradle.kts b/maps-mapbox/build.gradle.kts index 5f8027a..8fc8f18 100644 --- a/maps-mapbox/build.gradle.kts +++ b/maps-mapbox/build.gradle.kts @@ -3,7 +3,6 @@ */ plugins { - id("dev.icerock.mobile.multiplatform.android-manifest") id("dev.icerock.moko.gradle.multiplatform.mobile") id("dev.icerock.moko.gradle.publication") id("dev.icerock.moko.gradle.stub.javadoc") @@ -17,13 +16,13 @@ dependencies { commonMainApi(projects.maps) - "androidMainImplementation"(libs.appCompat) - "androidMainImplementation"(libs.lifecycle) - "androidMainImplementation"(libs.playServicesLocation) - "androidMainImplementation"(libs.mapboxAnnotation) - "androidMainImplementation"(libs.mapboxServices) - "androidMainApi"(libs.mapbox) - "androidMainApi"(libs.mapboxNavigation) + androidMainImplementation(libs.appCompat) + androidMainImplementation(libs.lifecycle) + androidMainImplementation(libs.playServicesLocation) + androidMainImplementation(libs.mapboxAnnotation) + androidMainImplementation(libs.mapboxServices) + androidMainApi(libs.mapbox) + androidMainApi(libs.mapboxNavigation) } cocoaPods { diff --git a/maps-mapbox/src/androidMain/kotlin/dev/icerock/moko/maps/mapbox/MapboxController.kt b/maps-mapbox/src/androidMain/kotlin/dev/icerock/moko/maps/mapbox/MapboxController.kt index 8783cb2..003725a 100644 --- a/maps-mapbox/src/androidMain/kotlin/dev/icerock/moko/maps/mapbox/MapboxController.kt +++ b/maps-mapbox/src/androidMain/kotlin/dev/icerock/moko/maps/mapbox/MapboxController.kt @@ -242,6 +242,7 @@ actual class MapboxController( fillOpacity(backgroundOpacity) ) + @Suppress("ArrayPrimitive") val lineLayer: LineLayer = LineLayer(lineLayerId, sourceId) .withProperties( lineWidth(lineWidth), @@ -287,7 +288,7 @@ actual class MapboxController( .accessToken(accessToken) .build() - @Suppress("BlockingMethodInNonBlockingContext") + @Suppress("BlockingMethodInNonBlockingContext", "UnsafeCallOnNullableType") val directionsRoute = withContext(Dispatchers.Default) { val result = directionsClient.executeCall() if (!result.isSuccessful) { @@ -362,6 +363,7 @@ actual class MapboxController( } } + @Suppress("ReturnCount") override suspend fun getSimilarNearAddresses( text: String?, maxResults: Int, @@ -373,14 +375,15 @@ actual class MapboxController( val locationProviderClient = locationHolder.get() val lastLocation: Location = suspendCoroutine { continuation -> + @Suppress("UnsafeCallOnNullableType") locationProviderClient.lastLocation.addOnCompleteListener { if (it.isSuccessful) { - continuation.resume(it.result!!) + continuation.resume(it.result) } else { continuation.resumeWithException(it.exception!!) } } - } + } ?: return emptyList() // TODO calculate bounds from radius @Suppress("MagicNumber") diff --git a/maps-mapbox/src/androidMain/kotlin/dev/icerock/moko/maps/mapbox/MapboxMarker.kt b/maps-mapbox/src/androidMain/kotlin/dev/icerock/moko/maps/mapbox/MapboxMarker.kt index 52d25c5..4689fef 100644 --- a/maps-mapbox/src/androidMain/kotlin/dev/icerock/moko/maps/mapbox/MapboxMarker.kt +++ b/maps-mapbox/src/androidMain/kotlin/dev/icerock/moko/maps/mapbox/MapboxMarker.kt @@ -31,7 +31,7 @@ actual class MapboxMarker( removeHandler.invoke(symbol) } - @ExperimentalTime + @OptIn(ExperimentalTime::class) override fun move(position: LatLng, rotation: Float, duration: Duration) { val currentPosition = symbol.latLng val newPosition = position.toMapboxLatLng() diff --git a/maps-mapbox/src/iosSimulatorArm64Main b/maps-mapbox/src/iosSimulatorArm64Main new file mode 120000 index 0000000..46be4b8 --- /dev/null +++ b/maps-mapbox/src/iosSimulatorArm64Main @@ -0,0 +1 @@ +iosX64Main \ No newline at end of file diff --git a/maps-mapbox/src/iosX64Main/kotlin/dev/icerock/moko/maps/mapbox/MapboxController.kt b/maps-mapbox/src/iosX64Main/kotlin/dev/icerock/moko/maps/mapbox/MapboxController.kt index 94ba0e9..51247f5 100644 --- a/maps-mapbox/src/iosX64Main/kotlin/dev/icerock/moko/maps/mapbox/MapboxController.kt +++ b/maps-mapbox/src/iosX64Main/kotlin/dev/icerock/moko/maps/mapbox/MapboxController.kt @@ -73,9 +73,9 @@ actual class MapboxController( actual suspend fun readUiSettings(): UiSettings { val mapView = weakMapView.get() + val compass = mapView?.compassView?.compassVisibility return UiSettings( - compassEnabled = mapView?.compassView?.compassVisibility != - MGLOrnamentVisibility.MGLOrnamentVisibilityHidden, + compassEnabled = compass != MGLOrnamentVisibility.MGLOrnamentVisibilityHidden, myLocationEnabled = mapView?.showsUserLocation ?: false, scrollGesturesEnabled = mapView?.scrollEnabled ?: false, zoomGesturesEnabled = mapView?.zoomEnabled ?: false, @@ -317,6 +317,7 @@ actual class MapboxController( mapView: MGLMapView, fillColorForPolygonAnnotation: MGLPolygon ): UIColor { + @Suppress("SwallowedException") return try { val settings = polygonSettings.getValue(fillColorForPolygonAnnotation.hashCode()) settings.fillColor @@ -330,6 +331,7 @@ actual class MapboxController( mapView: MGLMapView, strokeColorForShapeAnnotation: MGLShape ): UIColor { + @Suppress("SwallowedException") return try { val settings = polygonSettings.getValue(strokeColorForShapeAnnotation.hashCode()) settings.lineColor diff --git a/maps-mapbox/src/iosX64Main/kotlin/dev/icerock/moko/maps/mapbox/MapboxMarker.kt b/maps-mapbox/src/iosX64Main/kotlin/dev/icerock/moko/maps/mapbox/MapboxMarker.kt index da0372a..15f18a2 100644 --- a/maps-mapbox/src/iosX64Main/kotlin/dev/icerock/moko/maps/mapbox/MapboxMarker.kt +++ b/maps-mapbox/src/iosX64Main/kotlin/dev/icerock/moko/maps/mapbox/MapboxMarker.kt @@ -33,9 +33,9 @@ actual class MapboxMarker( override var rotation: Float get() = TODO("rotation not work for markers of Mapbox") - set(value) { TODO("rotation not work for markers of Mapbox") } + set(_) { TODO("rotation not work for markers of Mapbox") } - @ExperimentalTime + @OptIn(ExperimentalTime::class) override fun move(position: LatLng, rotation: Float, duration: Duration) { CATransaction.begin() CATransaction.setAnimationDuration(duration.inSeconds) diff --git a/maps/build.gradle.kts b/maps/build.gradle.kts index 77ca2bd..62a9bed 100644 --- a/maps/build.gradle.kts +++ b/maps/build.gradle.kts @@ -3,7 +3,6 @@ */ plugins { - id("dev.icerock.mobile.multiplatform.android-manifest") id("dev.icerock.moko.gradle.multiplatform.mobile") id("dev.icerock.moko.gradle.publication") id("dev.icerock.moko.gradle.stub.javadoc") diff --git a/maps/src/commonMain/kotlin/dev/icerock/moko/maps/Marker.kt b/maps/src/commonMain/kotlin/dev/icerock/moko/maps/Marker.kt index 96beea7..c3620b9 100644 --- a/maps/src/commonMain/kotlin/dev/icerock/moko/maps/Marker.kt +++ b/maps/src/commonMain/kotlin/dev/icerock/moko/maps/Marker.kt @@ -6,12 +6,10 @@ package dev.icerock.moko.maps import dev.icerock.moko.geo.LatLng import kotlin.time.Duration -import kotlin.time.ExperimentalTime interface Marker : MapElement { var position: LatLng var rotation: Float - @OptIn(ExperimentalTime::class) fun move(position: LatLng, rotation: Float = 0.0f, duration: Duration) } diff --git a/sample/android-app/build.gradle.kts b/sample/android-app/build.gradle.kts index f648223..a2c02f4 100644 --- a/sample/android-app/build.gradle.kts +++ b/sample/android-app/build.gradle.kts @@ -1,7 +1,6 @@ plugins { id("dev.icerock.moko.gradle.android.application") id("dev.icerock.moko.gradle.detekt") - id("kotlin-android") id("kotlin-kapt") } @@ -9,6 +8,8 @@ android { buildFeatures.dataBinding = true defaultConfig { + minSdk = 21 + applicationId = "dev.icerock.moko.samples.maps" versionCode = 1 @@ -16,12 +17,12 @@ android { multiDexEnabled = true -// val googleMapsApiKey: String = (System.getenv("GOOGLE_MAPS_API_KEY") ?: extra["googleMaps.apiKey"] as? String).orEmpty() -// val mapboxPublicToken: String = (System.getenv("MAPBOX_PUBLIC_TOKEN") ?: extra["mapbox.publicToken"] as? String).orEmpty() + val googleMapsApiKey: String = (System.getenv("GOOGLE_MAPS_API_KEY") ?: extra["googleMaps.apiKey"] as? String).orEmpty() + val mapboxPublicToken: String = (System.getenv("MAPBOX_PUBLIC_TOKEN") ?: extra["mapbox.publicToken"] as? String).orEmpty() -// manifestPlaceholders["googleMapsApiKey"] = googleMapsApiKey -// buildConfigField("String", "GOOGLE_MAPS_API_KEY", "\"$googleMapsApiKey\"") -// buildConfigField("String", "MAPBOX_PUBLIC_TOKEN", "\"$mapboxPublicToken\"") + manifestPlaceholders["googleMapsApiKey"] = googleMapsApiKey + buildConfigField("String", "GOOGLE_MAPS_API_KEY", "\"$googleMapsApiKey\"") + buildConfigField("String", "MAPBOX_PUBLIC_TOKEN", "\"$mapboxPublicToken\"") } } @@ -35,4 +36,5 @@ dependencies { implementation(libs.multidex) implementation(projects.sample.mppLibrary) + implementation(libs.mokoMvvmDataBinding) } diff --git a/sample/android-app/src/main/AndroidManifest.xml b/sample/android-app/src/main/AndroidManifest.xml index ee7681f..7e3ea69 100755 --- a/sample/android-app/src/main/AndroidManifest.xml +++ b/sample/android-app/src/main/AndroidManifest.xml @@ -9,10 +9,10 @@ - + diff --git a/sample/android-app/src/main/java/com/icerockdev/app/GoogleMapsActivity.kt b/sample/android-app/src/main/java/com/icerockdev/app/GoogleMapsActivity.kt index 0250d8c..a3e1e5d 100644 --- a/sample/android-app/src/main/java/com/icerockdev/app/GoogleMapsActivity.kt +++ b/sample/android-app/src/main/java/com/icerockdev/app/GoogleMapsActivity.kt @@ -9,7 +9,6 @@ import androidx.lifecycle.ViewModelProvider import com.google.android.gms.maps.SupportMapFragment import com.icerockdev.app.databinding.ActivityGoogleMapsBinding import com.icerockdev.library.GoogleMapViewModel -import dev.icerock.moko.geo.LocationTracker import dev.icerock.moko.maps.google.GoogleMapController import dev.icerock.moko.mvvm.MvvmActivity import dev.icerock.moko.mvvm.createViewModelFactory diff --git a/sample/android-app/src/main/java/com/icerockdev/app/MainActivity.kt b/sample/android-app/src/main/java/com/icerockdev/app/MainActivity.kt index 0fe5a01..440f142 100644 --- a/sample/android-app/src/main/java/com/icerockdev/app/MainActivity.kt +++ b/sample/android-app/src/main/java/com/icerockdev/app/MainActivity.kt @@ -26,4 +26,4 @@ class MainActivity : AppCompatActivity() { startActivity(Intent(this, MapboxActivity::class.java)) } } -} \ No newline at end of file +} diff --git a/sample/android-app/src/main/java/com/icerockdev/app/MapboxActivity.kt b/sample/android-app/src/main/java/com/icerockdev/app/MapboxActivity.kt index 2792e0a..e67c249 100644 --- a/sample/android-app/src/main/java/com/icerockdev/app/MapboxActivity.kt +++ b/sample/android-app/src/main/java/com/icerockdev/app/MapboxActivity.kt @@ -15,7 +15,6 @@ import dev.icerock.moko.mvvm.MvvmActivity import dev.icerock.moko.mvvm.createViewModelFactory import dev.icerock.moko.permissions.PermissionsController - class MapboxActivity : MvvmActivity() { override val layoutId: Int = R.layout.activity_mapbox override val viewModelVariableId: Int = BR.viewModel diff --git a/sample/mpp-library/build.gradle.kts b/sample/mpp-library/build.gradle.kts index e9c4288..d3f09e2 100644 --- a/sample/mpp-library/build.gradle.kts +++ b/sample/mpp-library/build.gradle.kts @@ -1,20 +1,27 @@ +import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget + plugins { - id("com.android.library") - id("dev.icerock.moko.gradle.android.base") - id("org.jetbrains.kotlin.multiplatform") - id("dev.icerock.mobile.multiplatform.targets") + id("dev.icerock.moko.gradle.multiplatform.mobile") id("dev.icerock.moko.gradle.detekt") - id("dev.icerock.mobile.multiplatform.android-manifest") id("dev.icerock.mobile.multiplatform.ios-framework") id("dev.icerock.mobile.multiplatform-resources") id("dev.icerock.mobile.multiplatform.cocoapods") } +kotlin.targets.withType() + .matching { it.konanTarget.family == org.jetbrains.kotlin.konan.target.Family.IOS } + .configureEach { + compilations.all { + kotlinOptions.freeCompilerArgs += "-Xoverride-konan-properties=osVersionMin.ios_x64=12.0;osVersionMin.ios_arm64=12.0;osVersionMin.ios_simulator_arm64=14.0" + } + } + dependencies { commonMainImplementation(libs.coroutines) commonMainImplementation(libs.mokoResources) commonMainApi(libs.mokoGeo) - commonMainApi(libs.mokoMvvm) + commonMainApi(libs.mokoMvvmCore) + commonMainApi(libs.mokoMvvmLiveData) commonMainApi(libs.mokoPermissions) commonMainApi(projects.maps) commonMainApi(projects.mapsGoogle) diff --git a/settings.gradle.kts b/settings.gradle.kts index 4c66b9c..0b7072a 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -8,7 +8,6 @@ enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS") dependencyResolutionManagement { repositories { mavenCentral() - mavenLocal() google() maven { @@ -25,12 +24,6 @@ dependencyResolutionManagement { ?: extra["mapbox.secretToken"] as? String } } - - jcenter { - content { - includeGroup("org.jetbrains.kotlinx") - } - } } } From 270ee3e1af10ec1be3062d3315ba8291b4a4b010 Mon Sep 17 00:00:00 2001 From: Aleksey Mikhailov Date: Fri, 1 Apr 2022 21:44:00 +0700 Subject: [PATCH 3/4] google maps xcframework integration (with ios simulator arm64 support) --- GoogleMapsXC.podspec | 24 ++++++ build.gradle.kts | 1 + gradle/libs.versions.toml | 2 +- maps-google/build.gradle.kts | 17 ++-- sample/ios-app/Podfile | 37 +++++---- sample/ios-app/Podfile.lock | 82 ++++++------------- .../TestProj.xcodeproj/project.pbxproj | 16 ---- sample/mpp-library/build.gradle.kts | 17 ++-- 8 files changed, 95 insertions(+), 101 deletions(-) create mode 100644 GoogleMapsXC.podspec diff --git a/GoogleMapsXC.podspec b/GoogleMapsXC.podspec new file mode 100644 index 0000000..ca73044 --- /dev/null +++ b/GoogleMapsXC.podspec @@ -0,0 +1,24 @@ +Pod::Spec.new do |s| + s.name = 'GoogleMapsXC' + s.version = "6.1.1-beta" + s.summary = 'GoogleMaps with xcframework' + s.description = 'GoogleMaps with xcframework description' + s.homepage = 'https://github.com/icerockdev/moko-maps' + s.license = { :type => 'UNKNOWN' } + s.authors = 'Google' + s.source = { + :http => "https://dl.google.com/geosdk/GoogleMaps-#{s.version}-xcframework.tar.gz", + :type => "tgz" + } + + s.platform = :ios + s.ios.deployment_target = '12.0' + + s.frameworks = ["Accelerate", "CoreData", "CoreGraphics", "CoreImage", "CoreLocation", "CoreTelephony", "CoreText", "GLKit", "ImageIO" "Metal", "OpenGLES", "QuartzCore", "SystemConfiguration", "UIKit"] + s.libraries = ["c++", "z"] + s.vendored_frameworks = [ + "GoogleMaps.xcframework", + "GoogleMapsBase.xcframework", + "GoogleMapsCore.xcframework" + ] +end diff --git a/build.gradle.kts b/build.gradle.kts index c2bd0b0..f7cacbb 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -7,6 +7,7 @@ buildscript { mavenCentral() google() gradlePluginPortal() + mavenLocal() } dependencies { diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index b0ec946..87fd702 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -61,7 +61,7 @@ androidGradlePlugin = { module = "com.android.tools.build:gradle", version = "7. googleServicesGradlePlugin = { module = "com.google.gms:google-services", version = "4.3.8" } firebaseGradlePlugin = { module = "com.google.firebase:firebase-crashlytics-gradle", version = "2.2.0" } mokoGradlePlugin = { module = "dev.icerock.moko:moko-gradle-plugin", version = "0.1.0" } -mobileMultiplatformGradlePlugin = { module = "dev.icerock:mobile-multiplatform", version = "0.14.1" } +mobileMultiplatformGradlePlugin = { module = "dev.icerock:mobile-multiplatform", version = "0.15.0" } mokoResourcesGeneratorGradlePlugin = { module = "dev.icerock.moko:resources-generator", version.ref = "mokoResourcesVersion" } kotlinSerializationGradlePlugin = { module = "org.jetbrains.kotlin:kotlin-serialization", version.ref = "kotlinVersion" } diff --git a/maps-google/build.gradle.kts b/maps-google/build.gradle.kts index f0a4316..cf98007 100644 --- a/maps-google/build.gradle.kts +++ b/maps-google/build.gradle.kts @@ -39,12 +39,19 @@ cocoaPods { extraLinkerOpts = listOf( "GoogleMapsBase", "GoogleMapsCore", "CoreGraphics", "QuartzCore", "UIKit", "ImageIO", "OpenGLES", "CoreData", "CoreText", "SystemConfiguration", "Security", - "CoreTelephony", "CoreImage" + "CoreTelephony", "CoreImage", "Metal" ).map { "-framework $it" } - ) { podsDir -> + ) { podsDir, target -> + val sdkPath = when (target.konanTarget) { + is org.jetbrains.kotlin.konan.target.KonanTarget.IOS_SIMULATOR_ARM64, + is org.jetbrains.kotlin.konan.target.KonanTarget.IOS_X64 -> "ios-x86_64_arm64-simulator" + is org.jetbrains.kotlin.konan.target.KonanTarget.IOS_ARM64 -> "ios-arm64" + else -> throw IllegalArgumentException("invalid target $target") + } listOf( - File(podsDir, "GoogleMaps/Base/Frameworks"), - File(podsDir, "GoogleMaps/Maps/Frameworks") - ) + "GoogleMapsBase", + "GoogleMapsCore", + "GoogleMaps" + ).map { File(podsDir, "GoogleMapsXC/$it.xcframework/$sdkPath") } } } diff --git a/sample/ios-app/Podfile b/sample/ios-app/Podfile index 48da49c..081c819 100644 --- a/sample/ios-app/Podfile +++ b/sample/ios-app/Podfile @@ -7,30 +7,31 @@ install! 'cocoapods', :disable_input_output_paths => true inhibit_all_warnings! use_frameworks! -platform :ios, '11.0' +platform :ios, '12.0' target 'TestProj' do # MultiPlatformLibrary pod 'MultiPlatformLibrary', :path => '../mpp-library' pod 'Mapbox-iOS-SDK', '6.3.0' pod 'MapboxNavigation', '1.4.1' - pod 'GoogleMaps', '5.1.0' +# pod 'GoogleMaps', '6.1.1' + pod 'GoogleMapsXC', :podspec => '../../GoogleMapsXC.podspec' end # GoogleMaps is static library that already linked in moko-maps-google. Remove duplicated linking. -post_install do |installer| - host_targets = installer.aggregate_targets.select { |aggregate_target| - aggregate_target.name.include? "Pods-" - } - - host_targets.each do |host_target| - host_target.xcconfigs.each do |config_name, config_file| - config_file.frameworks.delete("GoogleMaps") - config_file.frameworks.delete("GoogleMapsBase") - config_file.frameworks.delete("GoogleMapsCore") - - xcconfig_path = host_target.xcconfig_path(config_name) - config_file.save_as(xcconfig_path) - end - end -end \ No newline at end of file +# post_install do |installer| +# host_targets = installer.aggregate_targets.select { |aggregate_target| +# aggregate_target.name.include? "Pods-" +# } +# +# host_targets.each do |host_target| +# host_target.xcconfigs.each do |config_name, config_file| +# config_file.frameworks.delete("GoogleMaps") +# config_file.frameworks.delete("GoogleMapsBase") +# config_file.frameworks.delete("GoogleMapsCore") +# +# xcconfig_path = host_target.xcconfig_path(config_name) +# config_file.save_as(xcconfig_path) +# end +# end +# end \ No newline at end of file diff --git a/sample/ios-app/Podfile.lock b/sample/ios-app/Podfile.lock index 5055cbb..8436df4 100644 --- a/sample/ios-app/Podfile.lock +++ b/sample/ios-app/Podfile.lock @@ -1,75 +1,45 @@ PODS: - - GoogleMaps (3.7.0): - - GoogleMaps/Maps (= 3.7.0) - - GoogleMaps/Base (3.7.0) - - GoogleMaps/Maps (3.7.0): - - GoogleMaps/Base - - Mapbox-iOS-SDK (5.6.0): - - MapboxMobileEvents (= 0.10.2) - - MapboxAccounts (2.2.0) - - MapboxCoreNavigation (0.40.0): - - MapboxAccounts (~> 2.2.0) - - MapboxDirections (~> 0.31.0) - - MapboxMobileEvents (~> 0.10.2) - - MapboxNavigationNative (~> 6.2.1) - - Turf (~> 0.3.0) - - MapboxDirections (0.31.1): - - Polyline (~> 4.2) - - Turf (~> 0.3.0) - - MapboxMobileEvents (0.10.2) - - MapboxNavigation (0.40.0): - - Mapbox-iOS-SDK (~> 5.6) - - MapboxCoreNavigation (= 0.40.0) - - MapboxMobileEvents (~> 0.10.2) - - MapboxSpeech (~> 0.3.0) - - Solar (~> 2.1) - - MapboxNavigationNative (6.2.1) - - MapboxSpeech (0.3.1) + - GoogleMapsXC (6.1.1-beta) + - MapboxCommon (21.2.0) + - MapboxCoreMaps (10.4.1): + - MapboxCommon (~> 21.2) + - MapboxMaps (10.4.1): + - MapboxCommon (= 21.2.0) + - MapboxCoreMaps (= 10.4.1) + - MapboxMobileEvents (= 1.0.7) + - Turf (~> 2.0) + - MapboxMobileEvents (1.0.7) - MultiPlatformLibrary (0.1.0) - - Polyline (4.2.1) - - Solar (2.1.0) - - Turf (0.3.0) + - Turf (2.3.0) DEPENDENCIES: - - GoogleMaps (= 3.7.0) - - Mapbox-iOS-SDK (= 5.6.0) - - MapboxNavigation (= 0.40.0) + - GoogleMapsXC (from `../../GoogleMapsXC.podspec`) + - MapboxMaps (= 10.4.1) - MultiPlatformLibrary (from `../mpp-library`) SPEC REPOS: trunk: - - GoogleMaps - - Mapbox-iOS-SDK - - MapboxAccounts - - MapboxCoreNavigation - - MapboxDirections + - MapboxCommon + - MapboxCoreMaps + - MapboxMaps - MapboxMobileEvents - - MapboxNavigation - - MapboxNavigationNative - - MapboxSpeech - - Polyline - - Solar - Turf EXTERNAL SOURCES: + GoogleMapsXC: + :podspec: "../../GoogleMapsXC.podspec" MultiPlatformLibrary: :path: "../mpp-library" SPEC CHECKSUMS: - GoogleMaps: 55da829c68aa931f3ae982b9e341cc2f3d89c587 - Mapbox-iOS-SDK: 913c4655bd72a200f84996caf7a755b377abc77f - MapboxAccounts: f9716ba2cc66013e44ef93e0e16cfa1049f3aeec - MapboxCoreNavigation: 2856c46bd064bb11c406ba00191d579cda424efa - MapboxDirections: 69dca02d780c5e506bdcc65385dd5fb77b9e90b5 - MapboxMobileEvents: 2bc0ca2eedb627b73cf403258dce2b2fa98074a6 - MapboxNavigation: 42bae50b0381dce317c85884ba0de38fc68a4814 - MapboxNavigationNative: 11dc22140f4698d3f26989f2b6379dc81ef0d4c1 - MapboxSpeech: 67a558a672dc2b6266a993f4624895f69e58d105 + GoogleMapsXC: 2e3f87f4fbad69bc9cfd2dfecc14a0dfb3f1813b + MapboxCommon: eb826fdc45e8b77f87aca2519986d992fd4881b7 + MapboxCoreMaps: 13892567aa2a0e1cf8713887678f82998e2ff015 + MapboxMaps: 92e3eb21102beee5eca7cb4d9144162d6d1813c5 + MapboxMobileEvents: f7f3e8daeb4b83688ae62a4172dce79169a97233 MultiPlatformLibrary: 176fb8ade516666cd47e93de1b71ba0441a541bb - Polyline: 0e9890790292741c8186201a536b6bb6a78d02dd - Solar: 2dc6e7cc39186cb0c8228fa08df76fb50c7d8f24 - Turf: c6bdf62d6a70c647874f295dd1cf4eefc0c3e9e6 + Turf: d8d52444483c968cf2d65b8b54640f8faf4dda03 -PODFILE CHECKSUM: d3f063e08a8b62545e8d3ca223d36ca3c67d1c9c +PODFILE CHECKSUM: ebe7183c3b53507c7fc384f5ff7233a1ee3f79d3 -COCOAPODS: 1.10.0 +COCOAPODS: 1.11.2 diff --git a/sample/ios-app/TestProj.xcodeproj/project.pbxproj b/sample/ios-app/TestProj.xcodeproj/project.pbxproj index e1d55ef..8af04ef 100644 --- a/sample/ios-app/TestProj.xcodeproj/project.pbxproj +++ b/sample/ios-app/TestProj.xcodeproj/project.pbxproj @@ -115,7 +115,6 @@ 287627FB1F319065007FA12B /* Sources */, 287627FC1F319065007FA12B /* Frameworks */, 287627FD1F319065007FA12B /* Resources */, - F79810AA1499C35699C9419C /* [CP] Copy Pods Resources */, D87BA21237AC9858A1A613E3 /* [CP] Embed Pods Frameworks */, ); buildRules = ( @@ -212,21 +211,6 @@ shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; - F79810AA1499C35699C9419C /* [CP] Copy Pods Resources */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - name = "[CP] Copy Pods Resources"; - outputFileListPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-TestProj/Pods-TestProj-resources.sh\"\n"; - showEnvVarsInLog = 0; - }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ diff --git a/sample/mpp-library/build.gradle.kts b/sample/mpp-library/build.gradle.kts index d3f09e2..688cf9f 100644 --- a/sample/mpp-library/build.gradle.kts +++ b/sample/mpp-library/build.gradle.kts @@ -44,16 +44,23 @@ cocoaPods { precompiledPod( scheme = "GoogleMaps", onlyLink = true - ) { podsDir -> + ) { podsDir, target -> + val sdkPath = when (target.konanTarget) { + is org.jetbrains.kotlin.konan.target.KonanTarget.IOS_SIMULATOR_ARM64, + is org.jetbrains.kotlin.konan.target.KonanTarget.IOS_X64 -> "ios-x86_64_arm64-simulator" + is org.jetbrains.kotlin.konan.target.KonanTarget.IOS_ARM64 -> "ios-arm64" + else -> throw IllegalArgumentException("invalid target $target") + } listOf( - File(podsDir, "GoogleMaps/Base/Frameworks"), - File(podsDir, "GoogleMaps/Maps/Frameworks") - ) + "GoogleMapsBase", + "GoogleMapsCore", + "GoogleMaps" + ).map { File(podsDir, "GoogleMapsXC/$it.xcframework/$sdkPath") } } precompiledPod( scheme = "Mapbox", onlyLink = true - ) { podsDir -> + ) { podsDir, _ -> listOf(File(podsDir, "Mapbox-iOS-SDK/dynamic")) } } From 75d04c29a601487c2613d3e765daf9d96f258bd8 Mon Sep 17 00:00:00 2001 From: Aleksey Mikhailov Date: Fri, 1 Apr 2022 22:13:40 +0700 Subject: [PATCH 4/4] mapbox swift integration draft --- MapBoxWrapper.podspec | 19 + maps-mapbox/build.gradle.kts | 14 +- .../src/iosMain/swift/MapBoxWrapper.swift | 376 ++++++++++++++++++ sample/ios-app/Podfile | 4 +- sample/ios-app/Podfile.lock | 9 +- 5 files changed, 414 insertions(+), 8 deletions(-) create mode 100644 MapBoxWrapper.podspec create mode 100644 maps-mapbox/src/iosMain/swift/MapBoxWrapper.swift diff --git a/MapBoxWrapper.podspec b/MapBoxWrapper.podspec new file mode 100644 index 0000000..e27486f --- /dev/null +++ b/MapBoxWrapper.podspec @@ -0,0 +1,19 @@ +Pod::Spec.new do |s| + s.name = 'MapBoxWrapper' + s.version = "10.4.1" + s.summary = 'GoogleMaps with xcframework' + s.description = 'GoogleMaps with xcframework description' + s.homepage = 'https://github.com/icerockdev/moko-maps' + s.license = { :type => 'UNKNOWN' } + s.authors = 'Google' + s.source = { + :http => "none", + :type => "tgz" + } + + s.platform = :ios + s.ios.deployment_target = '12.0' + + s.dependency 'MapboxMaps', "#{s.version}" + s.source_files = 'maps-mapbox/src/iosMain/swift/*.swift' +end diff --git a/maps-mapbox/build.gradle.kts b/maps-mapbox/build.gradle.kts index 8fc8f18..79f4ae3 100644 --- a/maps-mapbox/build.gradle.kts +++ b/maps-mapbox/build.gradle.kts @@ -27,8 +27,16 @@ dependencies { cocoaPods { precompiledPod( - scheme = "Mapbox" - ) { podsDir -> - listOf(File(podsDir, "Mapbox-iOS-SDK/dynamic")) + scheme = "MapboxCoreMaps" + ) { podsDir, target -> + val sdkPath = when (target.konanTarget) { + is org.jetbrains.kotlin.konan.target.KonanTarget.IOS_SIMULATOR_ARM64, + is org.jetbrains.kotlin.konan.target.KonanTarget.IOS_X64 -> "ios-arm64_x86_64-simulator" + is org.jetbrains.kotlin.konan.target.KonanTarget.IOS_ARM64 -> "ios-arm64" + else -> throw IllegalArgumentException("invalid target $target") + } + listOf( + "MapboxCoreMaps" + ).map { File(podsDir, "$it/$it.xcframework/$sdkPath") } } } diff --git a/maps-mapbox/src/iosMain/swift/MapBoxWrapper.swift b/maps-mapbox/src/iosMain/swift/MapBoxWrapper.swift new file mode 100644 index 0000000..e006b0d --- /dev/null +++ b/maps-mapbox/src/iosMain/swift/MapBoxWrapper.swift @@ -0,0 +1,376 @@ +/* + * Copyright 2022 IceRock MAG Inc. Use of this source code is governed by the Apache 2.0 license. + */ + +import Foundation +import MapboxMaps +import CoreLocation +import MapKit + + +//class MapboxAnnotation : MGLPointAnnotation() { +// var onClick: (() -> Unit)? = null +// var image: UIImage? = null +//} + +//actual class MapboxMarker( +// private val annotation: MGLPointAnnotation, +// private val onDeleteCallback: (() -> Unit)? +//) : Marker { +// +// var onClick: (() -> Unit)? = null +// var image: UIImage? = null +// +// override fun delete() { +// onDeleteCallback?.invoke() +// } +// +// override var position: LatLng +// get() = annotation.coordinate.toLatLng() +// set(value) { +// annotation.setCoordinate(coordinate = value.toCoord2D()) +// } +// +// override var rotation: Float +// get() = TODO("rotation not work for markers of Mapbox") +// set(_) { TODO("rotation not work for markers of Mapbox") } +// +// @OptIn(ExperimentalTime::class) +// override fun move(position: LatLng, rotation: Float, duration: Duration) { +// CATransaction.begin() +// CATransaction.setAnimationDuration(duration.inSeconds) +// +// annotation.setCoordinate(coordinate = position.toCoord2D()) +// +// CATransaction.commit() +// } +// +// fun getAnnotation(): MGLPointAnnotation { +// return annotation +// } +//} + +@objc +public class MapboxSettingsNative: NSObject { + public var compassEnabled: Bool = false + public var myLocationEnabled: Bool = false + public var scrollGesturesEnabled: Bool = false + public var zoomGesturesEnabled: Bool = false + public var tiltGesturesEnabled: Bool = false + public var rotateGesturesEnabled: Bool = false + public var logoIsVisible: Bool = false + public var infoButtonIsVisible: Bool = false +} + +@objc +public class MapboxControllerNative: NSObject { + private let mapView: MapView + private let locationManager = CLLocationManager() + + init(mapView: MapView) { + self.mapView = mapView + } + + public func getSettings() -> MapboxSettingsNative { + let settings = MapboxSettingsNative() + + settings.compassEnabled = mapView.ornaments.options.compass.visibility != .hidden + settings.myLocationEnabled = false // mapView?.showsUserLocation ?: false, + settings.scrollGesturesEnabled = false // mapView?.scrollEnabled ?: false, + settings.zoomGesturesEnabled = false // mapView?.zoomEnabled ?: false, + settings.tiltGesturesEnabled = false // mapView?.pitchEnabled ?: false, + settings.rotateGesturesEnabled = false // mapView?.rotateEnabled ?: false, + settings.logoIsVisible = !mapView.ornaments.logoView.isHidden + settings.infoButtonIsVisible = !mapView.ornaments.attributionButton.isHidden + + return settings + } + + public func setSettings(_ settings: MapboxSettingsNative) { + if settings.compassEnabled { + mapView.ornaments.options.compass.visibility = .adaptive + } else { + mapView.ornaments.options.compass.visibility = .hidden + } + + // it.showsUserLocation = settings.myLocationEnabled + // it.scrollEnabled = settings.scrollGesturesEnabled + // it.rotateEnabled = settings.rotateGesturesEnabled + // it.zoomEnabled = settings.zoomGesturesEnabled + // it.pitchEnabled = settings.tiltGesturesEnabled + + mapView.ornaments.logoView.isHidden = !settings.logoIsVisible + mapView.ornaments.attributionButton.isHidden = !settings.infoButtonIsVisible + } +} + +//actual class MapboxController( +// mapView: MGLMapView +//) : MapController { +// +// private val locationManager = CLLocationManager() +// private val delegate = MapDelegate(this) +// +// private val weakMapView = WeakReference(mapView) +// +// private var isMapLoaded: Boolean = false +// +// private val markers: MutableMap = mutableMapOf() +// +// actual var onStartScrollCallback: ((isUserGesture: Boolean) -> Unit)? = null +// +// init { +// weakMapView.get()?.delegate = delegate +// } +// +// private fun getCurrentLocation(): LatLng { +// val location: CLLocation = weakMapView.get()?.userLocation?.location +// ?: locationManager.location +// ?: throw IllegalStateException("can't get location") +// +// return location.coordinate.toLatLng() +// } + +// override suspend fun getCurrentZoom(): Float { +// return weakMapView.get()?.zoomLevel?.toFloat() ?: 0f +// } +// +// override suspend fun setCurrentZoom(zoom: Float) { +// weakMapView.get()?.zoomLevel = zoom.toDouble() +// } +// +// override suspend fun getZoomConfig(): ZoomConfig { +// return ZoomConfig( +// min = weakMapView.get()?.minimumZoomLevel?.toFloat(), +// max = weakMapView.get()?.maximumZoomLevel?.toFloat() +// ) +// } +// +// override suspend fun setZoomConfig(config: ZoomConfig) { +// weakMapView.get()?.minimumZoomLevel = config.min?.toDouble() ?: DEFAULT_MINIMUM_ZOOM +// weakMapView.get()?.maximumZoomLevel = config.max?.toDouble() ?: DEFAULT_MAXIMUM_ZOOM +// } +// +// override suspend fun getMapCenterLatLng(): LatLng { +// return weakMapView.get()?.camera?.centerCoordinate?.toLatLng() ?: LatLng( +// latitude = 0.0, +// longitude = 0.0 +// ) +// } +// +// override fun showLocation(latLng: LatLng, zoom: Float, animation: Boolean) { +// weakMapView.get()?.setCenterCoordinate( +// centerCoordinate = latLng.toCoord2D(), +// zoomLevel = zoom.toDouble(), +// animated = if (isMapLoaded) { +// animation +// } else { +// false +// } +// ) +// } +// +// override fun showMyLocation(zoom: Float) { +// val location = getCurrentLocation() +// showLocation(latLng = location, zoom = zoom, animation = true) +// } +// +// override suspend fun addMarker( +// image: ImageResource, +// latLng: LatLng, +// rotation: Float, +// onClick: (() -> Unit)? +// ): Marker { +// val annotation = MGLPointAnnotation() +// annotation.setCoordinate(coordinate = latLng.toCoord2D()) +// +// // TODO: Need implementation for rotation +// if (rotation != 0.0f) println("WARNING: rotation not work for markers of Mapbox") +// +// val marker = MapboxMarker( +// annotation = annotation, +// onDeleteCallback = { +// weakMapView.get()?.removeAnnotation(annotation = annotation) +// markers.remove(annotation) +// } +// ) +// +// marker.onClick = onClick +// marker.image = image.toUIImage() +// markers.put(annotation, marker) +// +// weakMapView.get()?.addAnnotation(annotation) +// +// return marker +// } +// +// actual fun setStyleUrl(styleUrl: String) { +// weakMapView.get()?.styleURL = NSURL(string = styleUrl) +// } +// +// override suspend fun drawPolygon( +// pointList: List, +// backgroundColor: Color, +// lineColor: Color, +// backgroundOpacity: Float, +// lineWidth: Float, +// lineOpacity: Float, +// lineType: LineType +// ): MapElement { +// val polygon: MGLPolygonFeature = memScoped { +// +// val items = createValues(pointList.count()) { pos -> +// this.longitude = pointList[pos].longitude +// this.latitude = pointList[pos].latitude +// } +// MGLPolygonFeature.polygonWithCoordinates( +// coords = items.ptr, +// count = pointList.count().toULong() +// ) +// } +// val settings = MapboxPolygonSettings( +// fillColor = backgroundColor.toUIColor() +// .colorWithAlphaComponent(backgroundOpacity.toDouble()), +// lineColor = lineColor.toUIColor().colorWithAlphaComponent(lineOpacity.toDouble()) +// ) +// delegate.polygonSettings[polygon.hashCode()] = settings +// +// val source = MGLShapeSource( +// identifier = "line:${(0..Int.MAX_VALUE).random()}", +// shape = polygon, +// options = null +// ) +// delegate.style?.addSource(source) +// +// val layer = MGLLineStyleLayer( +// identifier = "line-layer:${(0..Int.MAX_VALUE).random()}", +// source = source +// ) +// if (lineType == LineType.DASHED) { +// layer.lineDashPattern = +// NSExpression.expressionForConstantValue(List(2) { 2.0 }) +// } +// layer.lineWidth = NSExpression.expressionForConstantValue(lineWidth) +// layer.lineColor = NSExpression.expressionForConstantValue( +// lineColor.toUIColor().colorWithAlphaComponent(lineOpacity.toDouble()) +// ) +// delegate.style?.addLayer(layer) +// +// weakMapView.get()?.addOverlay(polygon) +// return MapboxPolygon { +// weakMapView.get()?.removeOverlay(polygon) +// delegate.style?.removeLayer(layer) +// } +// } +// +// override suspend fun buildRoute( +// points: List, +// lineColor: Color, +// markersImage: ImageResource? +// ): MapElement { +// TODO("for now MapboxDirections pod can't be cinteroped, so on iOS this not implemented") +// } +// +// override suspend fun getAddressByLatLng(latitude: Double, longitude: Double): String? { +// TODO("for now MapboxDirections pod can't be cinteroped, so on iOS this not implemented") +// } +// +// override suspend fun getSimilarNearAddresses( +// text: String?, +// maxResults: Int, +// maxRadius: Int +// ): List { +// TODO("for now MapboxDirections pod can't be cinteroped, so on iOS this not implemented") +// } +// +// @Suppress("PARAMETER_NAME_CHANGED_ON_OVERRIDE", "CONFLICTING_OVERLOADS") +// private class MapDelegate( +// mapController: MapboxController +// ) : NSObject(), MGLMapViewDelegateProtocol { +// private val mapController = WeakReference(mapController) +// +// var polygonSettings: MutableMap = mutableMapOf() +// +// var style: MGLStyle? = null +// +// override fun mapViewDidFinishLoadingMap(mapView: MGLMapView) { +// mapController.get()?.isMapLoaded = true +// } +// +// override fun mapView(mapView: MGLMapView, didFinishLoadingStyle: MGLStyle) { +// style = didFinishLoadingStyle +// } +// +// override fun mapView( +// mapView: MGLMapView, +// regionWillChangeWithReason: MGLCameraChangeReason, +// animated: Boolean +// ) { +// when (regionWillChangeWithReason) { +// MGLCameraChangeReasonGesturePan, +// MGLCameraChangeReasonGestureZoomIn, +// MGLCameraChangeReasonGestureZoomOut, +// MGLCameraChangeReasonGesturePinch -> mapController.get()?.onStartScrollCallback?.invoke( +// true +// ) +// else -> mapController.get()?.onStartScrollCallback?.invoke(false) +// } +// } +// +// @Suppress("RETURN_TYPE_MISMATCH_ON_OVERRIDE") +// override fun mapView( +// mapView: MGLMapView, +// imageForAnnotation: MGLAnnotationProtocol +// ): MGLAnnotationImage? { +// val annotation = imageForAnnotation as MGLPointAnnotation +// val image = mapController.value?.markers?.get(annotation)?.image +// return if (image != null) { +// MGLAnnotationImage.annotationImageWithImage( +// image = image, +// reuseIdentifier = image.toString() +// ) +// } else { +// null +// } +// } +// +// // it also called for polygons (may be useful in feature) +// @Suppress("RETURN_TYPE_MISMATCH_ON_OVERRIDE") +// override fun mapView(mapView: MGLMapView, didSelectAnnotation: MGLAnnotationProtocol) { +// val annotation = (didSelectAnnotation as? MGLPointAnnotation) +// annotation?.let { mapController.value?.markers?.get(it)?.onClick?.invoke() } +// } +// +// override fun mapView( +// mapView: MGLMapView, +// fillColorForPolygonAnnotation: MGLPolygon +// ): UIColor { +// @Suppress("SwallowedException") +// return try { +// val settings = polygonSettings.getValue(fillColorForPolygonAnnotation.hashCode()) +// settings.fillColor +// } catch (exception: NoSuchElementException) { +// UIColor.blueColor() +// } +// } +// +// @Suppress("RETURN_TYPE_MISMATCH_ON_OVERRIDE") +// override fun mapView( +// mapView: MGLMapView, +// strokeColorForShapeAnnotation: MGLShape +// ): UIColor { +// @Suppress("SwallowedException") +// return try { +// val settings = polygonSettings.getValue(strokeColorForShapeAnnotation.hashCode()) +// settings.lineColor +// } catch (exception: NoSuchElementException) { +// UIColor.blueColor() +// } +// } +// } +// +// private companion object { +// const val DEFAULT_MINIMUM_ZOOM: Double = 0.0 +// const val DEFAULT_MAXIMUM_ZOOM: Double = 22.0 +// } +//} diff --git a/sample/ios-app/Podfile b/sample/ios-app/Podfile index 081c819..d9e8e4c 100644 --- a/sample/ios-app/Podfile +++ b/sample/ios-app/Podfile @@ -12,10 +12,8 @@ platform :ios, '12.0' target 'TestProj' do # MultiPlatformLibrary pod 'MultiPlatformLibrary', :path => '../mpp-library' - pod 'Mapbox-iOS-SDK', '6.3.0' - pod 'MapboxNavigation', '1.4.1' -# pod 'GoogleMaps', '6.1.1' pod 'GoogleMapsXC', :podspec => '../../GoogleMapsXC.podspec' + pod 'MapBoxWrapper', :path => '../..' end # GoogleMaps is static library that already linked in moko-maps-google. Remove duplicated linking. diff --git a/sample/ios-app/Podfile.lock b/sample/ios-app/Podfile.lock index 8436df4..58eb776 100644 --- a/sample/ios-app/Podfile.lock +++ b/sample/ios-app/Podfile.lock @@ -9,12 +9,14 @@ PODS: - MapboxMobileEvents (= 1.0.7) - Turf (~> 2.0) - MapboxMobileEvents (1.0.7) + - MapBoxWrapper (10.4.1): + - MapboxMaps (= 10.4.1) - MultiPlatformLibrary (0.1.0) - Turf (2.3.0) DEPENDENCIES: - GoogleMapsXC (from `../../GoogleMapsXC.podspec`) - - MapboxMaps (= 10.4.1) + - MapBoxWrapper (from `../..`) - MultiPlatformLibrary (from `../mpp-library`) SPEC REPOS: @@ -28,6 +30,8 @@ SPEC REPOS: EXTERNAL SOURCES: GoogleMapsXC: :podspec: "../../GoogleMapsXC.podspec" + MapBoxWrapper: + :path: "../.." MultiPlatformLibrary: :path: "../mpp-library" @@ -37,9 +41,10 @@ SPEC CHECKSUMS: MapboxCoreMaps: 13892567aa2a0e1cf8713887678f82998e2ff015 MapboxMaps: 92e3eb21102beee5eca7cb4d9144162d6d1813c5 MapboxMobileEvents: f7f3e8daeb4b83688ae62a4172dce79169a97233 + MapBoxWrapper: d952f9a084aea5299486eea8d94038d16a3bcf20 MultiPlatformLibrary: 176fb8ade516666cd47e93de1b71ba0441a541bb Turf: d8d52444483c968cf2d65b8b54640f8faf4dda03 -PODFILE CHECKSUM: ebe7183c3b53507c7fc384f5ff7233a1ee3f79d3 +PODFILE CHECKSUM: 691d6710edfabbb8953cf43a7c96da641f6249a8 COCOAPODS: 1.11.2