Conversation
…1716) - Consolidate Kotlin Extensions (KTX into Utils): Move all reactive Coroutine/Flow extensions (awaitMap, mapClickEvents, cameraMoveEvents) and option builder DSLs (addMarker, addPolyline, addPolygon) from android-maps-ktx directly into android-maps-utils. - Canonical Non-KTX Packages: Place all reactive Coroutine/Flow extensions and DSL builders in canonical com.google.maps.android.* packages. - Deprecated Compatibility Layer: Preserve the legacy com.google.maps.android.ktx.* package structure with @deprecated(level = DeprecationLevel.WARNING, replaceWith = ReplaceWith(...)) forwarding wrappers and typealiases so existing imports compile seamlessly with deprecation warnings. - Multi-Module Integration: Integrate KTX extensions across :library, :clustering, :heatmaps, and :data modules. - Demo & Test Consolidation: Include KtxExtensionsDemoActivity in :demo and integrate all 18 KTX unit test suites with both canonical and shim test coverage.
d5b5d7a to
f4a5753
Compare
…1716) - Consolidate Kotlin Extensions (KTX into Utils): Move all reactive Coroutine/Flow extensions (awaitMap, mapClickEvents, cameraMoveEvents) and option builder DSLs (addMarker, addPolyline, addPolygon) from android-maps-ktx directly into android-maps-utils. - Canonical Non-KTX Packages: Place all reactive Coroutine/Flow extensions and DSL builders in canonical com.google.maps.android.* packages. - Deprecated Compatibility Layer: Preserve the legacy com.google.maps.android.ktx.* package structure with @deprecated(level = DeprecationLevel.WARNING, replaceWith = ReplaceWith(...)) forwarding wrappers and typealiases so existing imports compile seamlessly with deprecation warnings. - Multi-Module Integration: Integrate KTX extensions across :library, :clustering, :heatmaps, and :data modules. - Demo & Test Consolidation: Include KtxExtensionsDemoActivity in :demo and integrate all 18 KTX unit test suites with both canonical and shim test coverage.
f4a5753 to
407fded
Compare
Code Coverage
Files |
…1716) - Consolidate Kotlin Extensions (KTX into Utils): Move all reactive Coroutine/Flow extensions (awaitMap, mapClickEvents, cameraMoveEvents) and option builder DSLs (addMarker, addPolyline, addPolygon) from android-maps-ktx directly into android-maps-utils. - Canonical Non-KTX Packages: Place all reactive Coroutine/Flow extensions and DSL builders in canonical com.google.maps.android.* packages. - Deprecated Compatibility Layer: Preserve the legacy com.google.maps.android.ktx.* package structure with @deprecated(level = DeprecationLevel.WARNING, replaceWith = ReplaceWith(...)) forwarding wrappers and typealiases so existing imports compile seamlessly with deprecation warnings. - Multi-Module Integration: Integrate KTX extensions across :library, :clustering, :heatmaps, and :data modules. - Demo & Test Consolidation: Include KtxExtensionsDemoActivity in :demo and integrate all 18 KTX unit test suites with both canonical and shim test coverage.
407fded to
96b0801
Compare
…1716) - Consolidate Kotlin Extensions (KTX into Utils): Move all reactive Coroutine/Flow extensions (awaitMap, mapClickEvents, cameraMoveEvents) and option builder DSLs (addMarker, addPolyline, addPolygon) from android-maps-ktx directly into android-maps-utils. - Canonical Non-KTX Packages: Place all reactive Coroutine/Flow extensions and DSL builders in canonical com.google.maps.android.* packages. - Deprecated Compatibility Layer: Preserve the legacy com.google.maps.android.ktx.* package structure with @deprecated(level = DeprecationLevel.WARNING, replaceWith = ReplaceWith(...)) forwarding wrappers and typealiases so existing imports compile seamlessly with deprecation warnings. - Multi-Module Integration: Integrate KTX extensions across :library, :clustering, :heatmaps, and :data modules. - Demo & Test Consolidation: Include KtxExtensionsDemoActivity in :demo and integrate all 18 KTX unit test suites with both canonical and shim test coverage.
96b0801 to
ff8f979
Compare
…1716) - Consolidate Kotlin Extensions (KTX into Utils): Move all reactive Coroutine/Flow extensions (awaitMap, mapClickEvents, cameraMoveEvents) and option builder DSLs (addMarker, addPolyline, addPolygon) from android-maps-ktx directly into android-maps-utils. - Canonical Non-KTX Packages: Place all reactive Coroutine/Flow extensions and DSL builders in canonical com.google.maps.android.* packages. - Deprecated Compatibility Layer: Preserve the legacy com.google.maps.android.ktx.* package structure with @deprecated(level = DeprecationLevel.WARNING, replaceWith = ReplaceWith(...)) forwarding wrappers and typealiases so existing imports compile seamlessly with deprecation warnings. - Multi-Module Integration: Integrate KTX extensions across :library, :clustering, :heatmaps, and :data modules. - Demo & Test Consolidation: Include KtxExtensionsDemoActivity in :demo and integrate all 18 KTX unit test suites with both canonical and shim test coverage.
d00d357 to
f1a4793
Compare
…1716) - Consolidate Kotlin Extensions (KTX into Utils): Move all reactive Coroutine/Flow extensions (awaitMap, mapClickEvents, cameraMoveEvents) and option builder DSLs (addMarker, addPolyline, addPolygon) from android-maps-ktx directly into android-maps-utils. - Canonical Non-KTX Packages: Place all reactive Coroutine/Flow extensions and DSL builders in canonical com.google.maps.android.* packages. - Deprecated Compatibility Layer: Preserve the legacy com.google.maps.android.ktx.* package structure with @deprecated(level = DeprecationLevel.WARNING, replaceWith = ReplaceWith(...)) forwarding wrappers and typealiases so existing imports compile seamlessly with deprecation warnings. - Multi-Module Integration: Integrate KTX extensions across :library, :clustering, :heatmaps, and :data modules. - Demo & Test Consolidation: Include KtxExtensionsDemoActivity in :demo and integrate all 18 KTX unit test suites with both canonical and shim test coverage.
5cdce08 to
3ce966a
Compare
…1716) - Consolidate Kotlin Extensions (KTX into Utils): Move all reactive Coroutine/Flow extensions (awaitMap, mapClickEvents, cameraMoveEvents) and option builder DSLs (addMarker, addPolyline, addPolygon) from android-maps-ktx directly into android-maps-utils. - Canonical Non-KTX Packages: Place all reactive Coroutine/Flow extensions and DSL builders in canonical com.google.maps.android.* packages. - Deprecated Compatibility Layer: Preserve the legacy com.google.maps.android.ktx.* package structure with @deprecated(level = DeprecationLevel.WARNING, replaceWith = ReplaceWith(...)) forwarding wrappers and typealiases so existing imports compile seamlessly with deprecation warnings. - Multi-Module Integration: Integrate KTX extensions across :library, :clustering, :heatmaps, and :data modules. - Demo & Test Consolidation: Include KtxExtensionsDemoActivity in :demo and integrate all 18 KTX unit test suites with both canonical and shim test coverage.
3ce966a to
5f6c90f
Compare
…1716) - Consolidate Kotlin Extensions (KTX into Utils): Move all reactive Coroutine/Flow extensions (awaitMap, mapClickEvents, cameraMoveEvents) and option builder DSLs (addMarker, addPolyline, addPolygon) from android-maps-ktx directly into android-maps-utils. - Canonical Non-KTX Packages: Place all reactive Coroutine/Flow extensions and DSL builders in canonical com.google.maps.android.* packages. - Deprecated Compatibility Layer: Preserve the legacy com.google.maps.android.ktx.* package structure with @deprecated(level = DeprecationLevel.WARNING, replaceWith = ReplaceWith(...)) forwarding wrappers and typealiases so existing imports compile seamlessly with deprecation warnings. - Multi-Module Integration: Integrate KTX extensions across :library, :clustering, :heatmaps, and :data modules. - Demo & Test Consolidation: Include KtxExtensionsDemoActivity in :demo and integrate all 18 KTX unit test suites with both canonical and shim test coverage.
5f6c90f to
a2164ec
Compare
…1716) - Consolidate Kotlin Extensions (KTX into Utils): Move all reactive Coroutine/Flow extensions (awaitMap, mapClickEvents, cameraMoveEvents) and option builder DSLs (addMarker, addPolyline, addPolygon) from android-maps-ktx directly into android-maps-utils. - Canonical Non-KTX Packages: Place all reactive Coroutine/Flow extensions and DSL builders in canonical com.google.maps.android.* packages. - Deprecated Compatibility Layer: Preserve the legacy com.google.maps.android.ktx.* package structure with @deprecated(level = DeprecationLevel.WARNING, replaceWith = ReplaceWith(...)) forwarding wrappers and typealiases so existing imports compile seamlessly with deprecation warnings. - Multi-Module Integration: Integrate KTX extensions across :library, :clustering, :heatmaps, and :data modules. - Demo & Test Consolidation: Include KtxExtensionsDemoActivity in :demo and integrate all 18 KTX unit test suites with both canonical and shim test coverage.
…aps-ktx - Add canonical Context.awaitMapsSdkInitialized(preferredRenderer) suspending extension in com.google.maps.android. - Add deprecated backward-compatibility shim in com.google.maps.android.ktx. - Add canonical and shim unit test suites for MapsInitializer coroutine extensions. - Showcase awaitMapsSdkInitialized in KtxExtensionsDemoActivity and register demo in MainActivity. - Update README.md documentation with awaitMapsSdkInitialized usage example.
…me for unit tests
| ### Bug Fixes | ||
|
|
||
| * prevent StackOverflowError when parsing deeply nested KML containers and multi-geometries ([#1710](https://github.com/googlemaps/android-maps-utils/issues/1710)) ([1463cc5](https://github.com/googlemaps/android-maps-utils/commit/1463cc572da85b8a2317690fa94b0e2432995a96)) | ||
| >>>>>>> origin/main |
There was a problem hiding this comment.
is there any purpose for adding this ?
| } | ||
| } | ||
| systemProperty("user.home", testHome.absolutePath) | ||
| environment("ANDROID_HOME", System.getenv("ANDROID_HOME") ?: "/usr/local/google/home/dkhawk/Android/Sdk") |
There was a problem hiding this comment.
Do we really need this "/usr/local/google/home/dkhawk/Android/Sdk" path ?
| public fun GoogleMap.cameraEvents(): Flow<CameraEvent> = this.canonicalCameraEvents() | ||
|
|
||
| @Deprecated("Moved to com.google.maps.android.awaitAnimateCamera", ReplaceWith("awaitAnimateCamera(cameraUpdate, durationMs)", "com.google.maps.android.awaitAnimateCamera")) | ||
| public suspend inline fun GoogleMap.awaitAnimateCamera(cameraUpdate: CameraUpdate, durationMs: Int = 3000): Unit = this.canonicalAwaitAnimateCamera(cameraUpdate, durationMs) |
There was a problem hiding this comment.
When calling awaitAnimateCamera(), we now need to pass 0 if we don't want to set a duration. Is there a reason we didn't make this parameter optional?
| */ | ||
| public suspend inline fun GoogleMap.awaitAnimateCamera( | ||
| cameraUpdate: CameraUpdate, | ||
| durationMs: Int = 3000 |
There was a problem hiding this comment.
When calling awaitAnimateCamera(), we now need to pass 0 if we don't want to set a duration. Is there a reason we didn't make this parameter optional ?
| var callbackInvoked = false | ||
| val status = MapsInitializer.initialize(this, preferredRenderer) { renderer -> | ||
| callbackInvoked = true | ||
| continuation.resume(renderer) |
There was a problem hiding this comment.
If status != ConnectionResult.SUCCESS, the continuation resumes with an exception immediately. If Play Services later invokes the OnMapsSdkInitializedCallback asynchronously (or if the coroutine was cancelled while waiting), calling continuation.resume(renderer) without checking if (continuation.isActive), it might be throw IllegalStateException
What did you think on this ??
Fix: Guard resumption with if (continuation.isActive) continuation.resume(...), and add continuation.invokeOnCancellation { setOnMapLoadedCallback(null) } to awaitMapLoad()
| LocationManager.PASSIVE_PROVIDER | ||
| } | ||
|
|
||
| requestLocationUpdates(provider, minTimeMs, minDistanceM, listener) |
There was a problem hiding this comment.
Unlike FusedLocationProvider.kt(which defaults to looper: Looper = Looper.getMainLooper()), LocationManager.coarseLocationEvents and fineLocationEvents call the 4-argument requestLocationUpdates(provider, minTimeMs, minDistanceM, listener) without a Looper. If collected on Dispatchers.IO or Dispatchers.Default, LocationManager calls Looper.myLooper() and crashes immediately with RuntimeException: Can't create handler inside thread that has not called Looper.prepare().
Fix: Add looper: Looper = Looper.getMainLooper() parameter to coarseLocationEvents and fineLocationEvents and pass it to requestLocationUpdates(provider, minTimeMs, minDistanceM, listener, looper).
| } | ||
| } | ||
|
|
||
| requestLocationUpdates(LocationManager.GPS_PROVIDER, minTimeMs, minDistanceM, listener) |
There was a problem hiding this comment.
Unlike FusedLocationProvider.kt(which defaults to looper: Looper = Looper.getMainLooper()), LocationManager.coarseLocationEvents and fineLocationEvents call the 4-argument requestLocationUpdates(provider, minTimeMs, minDistanceM, listener) without a Looper. If collected on Dispatchers.IO or Dispatchers.Default, LocationManager calls Looper.myLooper() and crashes immediately with RuntimeException: Can't create handler inside thread that has not called Looper.prepare().
Fix: Add looper: Looper = Looper.getMainLooper() parameter to coarseLocationEvents and fineLocationEvents and pass it to requestLocationUpdates(provider, minTimeMs, minDistanceM, listener, looper).
| * **How we know it is correct:** Asserts the emitted coordinates equal the exact `target` LatLng. | ||
| */ | ||
| @Test | ||
| public fun testMapClickEvents(): Unit = runTest { |
There was a problem hiding this comment.
Currently all test cases are false positive.
If we pass assertThat(event).isEqualTo(LatLng(9999.0, 9999.0)) or even if onMapClick(target) is never called at all, the test still pass, because the assertion line is dead code that gets cancelled before execution.
Solution :
Use async { ... } and deferred.await()
Using async instead of launch is cleaner, shorter, and eliminates manual job.cancel() calls altogether:
@test
public fun testMapClickEvents(): Unit = runTest {
val target = LatLng(10.0, 20.0)
val deferred = async {
googleMap.mapClickEvents().first()
}
advanceUntilIdle()
verify(googleMap).setOnMapClickListener(mapClickListener.capture())
mapClickListener.value.onMapClick(target)
assertThat(deferred.await()).isEqualTo(target)
}
Note : We need to do similar kind of handling for all the test cases
| } | ||
|
|
||
| @Test | ||
| public fun testClusterInfoWindowClickEvents(): Unit = runTest { |
There was a problem hiding this comment.
Currently all test cases are false positive.
If we pass any cluster value still test will pass, because the assertion line is dead code that gets cancelled before execution.
Solution :
Use async { ... } and deferred.await()
Using async instead of launch is cleaner, shorter, and eliminates manual job.cancel() calls altogether:
@test
public fun testClusterInfoWindowClickEvents(): Unit = runTest {
val deferred = async {
clusterManager.clusterInfoWindowClickEvents().first()
}
advanceUntilIdle()
verify(clusterManager).setOnClusterInfoWindowClickListener(clusterInfoWindowClickListener.capture())
clusterInfoWindowClickListener.value.onClusterInfoWindowClick(cluster)
assertThat(deferred.await()).isEqualTo(cluster)
}
Note : We need to do similar kind of handling for all the test cases
| } | ||
|
|
||
| @Test | ||
| public fun testMarkerCollectionClickEvents(): Unit = runTest { |
There was a problem hiding this comment.
test case is false positive.
If we pass any Marker value here, still this test will pass, because the assertion line is dead code that gets cancelled before execution.
Solution :
Use async { ... } and deferred.await()
Using async instead of launch is cleaner, shorter, and eliminates manual job.cancel() calls altogether:
@Test
public fun testMarkerCollectionClickEvents(): Unit = runTest {
val deferred = async {
markerCollection.clickEvents().first()
}
advanceUntilIdle()
// Trigger the event via our tracked active listener slot!
assertThat(activeMarkerClickListener).isNotNull()
activeMarkerClickListener?.onMarkerClick(marker)
assertThat(deferred.await()).isEqualTo(marker)
}
Note : We need to do similar kind of handling in other test cases also
| espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espresso-core" } | ||
| junit = { module = "junit:junit", version.ref = "junit" } | ||
| mockito-core = { module = "org.mockito:mockito-core", version.ref = "mockito-core" } | ||
| mockito-kotlin = { module = "com.nhaarman.mockitokotlin2:mockito-kotlin", version.ref = "mockito-kotlin" } |
There was a problem hiding this comment.
any specific reason, why we use this instead of "org.mockito.kotlin:mockito-kotlin" lib ?
| dependsOn(generateArtifactIdFile) | ||
| } | ||
|
|
||
| tasks.named("dokkaGeneratePublicationHtml") { |
Summary
Migrates all Kotlin extensions from
android-maps-ktx(maps-ktxandmaps-utils-ktx) directly intoandroid-maps-utils, establishingv6.0.0as the consolidated, single-source release for both Java utilities and Kotlin Coroutine/Flow extensions.Key Changes
awaitMap(),awaitMapsSdkInitialized(),awaitAnimateCamera()).Flowevent streams (mapClickEvents(),cameraMoveEvents(),markerClickEvents()).addMarker { ... },addPolyline { ... },addPolygon { ... }).com.google.maps.android.*packages.com.google.maps.android.ktx.*with@Deprecated(level = DeprecationLevel.WARNING)forwarding wrappers and typealiases for seamless backward compatibility.release-please-config.jsonand.release-please-manifest.jsonfor finalv6.0.0stable release.README.md,MIGRATION.md,llm-integration-prompt.md,.gemini/skills/android-maps-utils/SKILL.md).main(e8ef093c, Kover migration).:library,:clustering,:data,:heatmaps,:ui,:demo).