Repository navigation
Conversation
61326dc to
d1f772a
Compare
Coverage (unit tests)Compared against
Line and branch coverage from unit test reports. History is recorded on the |
d1f772a to
4b87319
Compare
4b87319 to
12eb5aa
Compare
12eb5aa to
ae29c41
Compare
ae29c41 to
c9b2c1f
Compare
Coverage (instrumentation tests)
Files
|
| textColor: Color = DarkGray, | ||
| lineColor: Color = DarkGray, | ||
| shadowColor: Color = Color.White, | ||
| measureAtCenter: Boolean = true, |
There was a problem hiding this comment.
Adding measureAtCenter changes the JVM signature of both ScaleBar and DisappearingScaleBar (the old ones are gone from maps-compose-widgets.api), so an app or library compiled against 9.x would fail with NoSuchMethodError at runtime. Could we keep the previous signatures as @Deprecated(level = DeprecationLevel.HIDDEN) overloads that forward to the new ones? That keeps the change additive, as AGENTS.md asks.
There was a problem hiding this comment.
Good catch! Fixed in f4607747 — added @Deprecated("Retained for binary compatibility", level = DeprecationLevel.HIDDEN) overloads for both ScaleBar and DisappearingScaleBar that delegate to the new functions with measureAtCenter = true, restoring ScaleBar-7pV8VcA and DisappearingScaleBar-JXI6SxA in maps-compose-widgets.api.
| // Read both position and isMoving so the scale updates whenever the camera pans, | ||
| // zooms, rotates, tilts, or finishes an animation/gesture. | ||
| val position = cameraPositionState.position | ||
| cameraPositionState.isMoving |
There was a problem hiding this comment.
Is there a reason why we read isMoving here? position already changes on every camera update, so I think this only adds one more recalculation when the gesture ends, with the same projection.
There was a problem hiding this comment.
Good point! Fixed in f4607747 — cameraPositionState.position already updates on every camera movement frame, so reading isMoving only triggered an extra recalculation when the gesture ended. Removed cameraPositionState.isMoving from derivedStateOf in both ScaleBar.kt and ScaleBarActivity.kt.
| <activity android:name="androidx.activity.ComponentActivity" /> | ||
| <activity | ||
| android:name="androidx.activity.ComponentActivity" | ||
| android:configChanges="orientation|screenSize|screenLayout|smallestScreenSize" /> |
There was a problem hiding this comment.
This applies to every test in maps-app that uses createComposeRule(), not only the scale bar ones: none of them will be recreated on rotation anymore. Could we use a dedicated test activity with these configChanges for the in-place rotation test instead?
There was a problem hiding this comment.
Fixed in f4607747 — restored <activity android:name="androidx.activity.ComponentActivity" /> without configChanges in AndroidManifest.xml, and added a dedicated InPlaceRotationTestActivity with android:configChanges="orientation|screenSize|screenLayout|smallestScreenSize" used only by ScaleBarTests.
| } | ||
|
|
||
| @Test | ||
| public fun testScaleBarCalculationAfterDeviceRotationToLandscape() { |
There was a problem hiding this comment.
nit: both orientations return the same LatLngs from the mock, so the results are equal by construction. I think this mostly repeats testScaleBarCalculationAtTargetCenter with different points. Should we drop it, or make the landscape projection return a different span?
There was a problem hiding this comment.
Fixed in f4607747 — updated testScaleBarCalculationAfterDeviceRotationToLandscape so the portrait and landscape screen coordinates in the mock Projection return distinct LatLng spans, verifying that calculateDistance queries the updated landscapeCenter coordinates and produces the corresponding landscape span.
kikoso
left a comment
There was a problem hiding this comment.
Can you take a look at the comments, @dkhawk ? Since this adds a public parameter and changes where the scale is measured by default, should we title it feat(widgets): instead of refactor? Otherwise release-please won't list it.
Adopt the Units API from android-maps-utils (MetricUnitsConverter, ImperialUnitsConverter with a 1-mile threshold, NumberFormatSymbols.COMPACT, and toDistanceString) for formatting metric and imperial distances in ScaleBar. Also fix a pre-existing accuracy bug in ScaleBar where the ground distance was previously calculated between Point(0, 0) and Point(widthInPixels, 0) at the top-left corner of the viewport and only recomputed when cameraPositionState.position.zoom changed: - Because Google Maps uses a conformal Web Mercator projection (EPSG:3857), map scale stretches isotropically in both latitude (North-South) and longitude (East-West) by a factor of 1 / cos(latitude) as you move toward the poles. Measuring at the top corner of the screen rather than the center of the map caused the scale bar to reflect the scale at the top edge of the viewport instead of where the camera is focused (for example, when centered on Iceland at zoom 3.15, the top edge of the screen lies in northern Greenland near 82N, causing the top-corner calculation to report 140 km / 87 mi instead of the 429 km / 267 mi width at Iceland). - Measure the scale across the camera target (cameraPositionState.position.target) at the center of the viewport by default, and observe cameraPositionState.position, cameraPositionState.isMoving, and LocalConfiguration.current so the scale bar stays accurate across latitude panning, map bearing rotation, camera tilt, and device orientation changes. - Add a measureAtCenter: Boolean = true parameter to ScaleBar and DisappearingScaleBar so callers can preserve the legacy top-corner behavior (measureAtCenter = false) if desired. - Update ScaleBarActivity with three sets of East-West and North-South reference crosshairs (at 10%, 50%, and 90% of the viewport height) whose ground lengths match the ScaleBar distance, plus UI checkboxes to toggle 'Measure at center' and 'Show crosshairs'. - Add unit and instrumentation tests covering Units API formatting, latitude changes at constant zoom, map bearing rotation, device rotation, and legacy top-corner measurement.
…lity and tests - Add @deprecated(level = DeprecationLevel.HIDDEN) overloads for ScaleBar and DisappearingScaleBar to preserve binary compatibility with pre-existing callers - Remove redundant cameraPositionState.isMoving reads from derivedStateOf blocks in ScaleBar and ScaleBarActivity - Restore generic ComponentActivity in AndroidManifest.xml and add dedicated InPlaceRotationTestActivity for ScaleBarTests - Use distinct portrait and landscape screen coordinates in ScaleBarUnitTest mock Projection - Migrate ScaleBarActivity imports from material to material3 to resolve UsingMaterialAndMaterial3Libraries lint warnings
f460774 to
1b7b62f
Compare
Summary
Adopt the
android-maps-utilsUnits API inScaleBar:ScaleBarwithMetricUnitsConverter,ImperialUnitsConverter(milesThreshold = 1.miles),NumberFormatSymbols.COMPACT, andLength.toDistanceString(...).1.miles(5,280 ft) threshold so the imperial scale bar transitions from feet to miles at 1 mile, matching the metric transition from meters to kilometers at 1 km (1,000 m).Fix
ScaleBarground distance calculation to measure at the center of the map viewport (measureAtCenter = true):calculateDistancecomputed the ground distance betweenPoint(0, 0)andPoint(widthInPixels, 0)at the top-left corner of the map viewport, and only recomputed whencameraPositionState.position.zoomchanged.EPSG:3857), which stretches map scale isotropically in both latitude (North–South) and longitude (East–West) by a factor of1 / cos(latitude)as you move toward the poles. Because of this stretching, the ground distance per pixel at the top corner of the screen can differ dramatically from the center of the screen where the user is looking.~65° N) atzoom = 3.15, theScaleBarwidth at the center of the screen spans the full coast-to-coast width of Iceland (429 km/267 mi). However, the top corner of the screen (Point(0, 0)) lies up in northern Greenland (~82° N), causing the top-corner calculation to report only140 km/87 mi(over3xsmaller than the scale at the center of the map).measureAtCenter: Boolean = true),ScaleBarandDisappearingScaleBarnow projectcameraPositionState.position.targetto screen coordinates and sample the horizontal pixel span across the center of the viewport (centerPoint.y).cameraPositionState.position,cameraPositionState.isMoving, andLocalConfiguration.currentinsidederivedStateOfso the scale bar updates accurately across latitude panning at constant zoom, map bearing rotation, camera tilt, and device orientation changes.measureAtCenter: Boolean = truetoScaleBarandDisappearingScaleBarso callers can preserve the legacy top-corner behavior (measureAtCenter = false) if desired.Interactive visual verification in
ScaleBarActivity:10%,50%, and90%of the viewport height, where each line's ground length on Earth equals the exact distance reported by theScaleBar.ScaleBar.Testing
:maps-app:connectedDebugAndroidTest&:maps-compose-widgets:connectedDebugAndroidTest):ScaleBarTests: Verifies initial state, fractional kilometers (< 10 km), fractional miles (< 10 mi), latitude changes at constant zoom (Equator vs. Iceland), map bearing rotation (0°,45°,90°,180°,270°), in-place and full-Activity device rotation (Portrait <-> Landscape), and legacy top-corner measurement (measureAtCenter = false).ScaleBarUnitTest: VerifiescalculateDistancecentered at target, legacy top-left fallback, and portrait-to-landscape viewport rotation.:maps-app:testDebugUnitTest):ScaleBarRenderingTests: VerifiesformatMetricDistanceandformatImperialDistanceacross meters/kilometers and feet/miles thresholds.