Repository navigation
Conversation
Plumbs an optional Location through CameraSystem.takePicture and startVideoRecording into CameraX ImageCapture metadata and video output options. CaptureControllerImpl reads the current location from an optional LocationProvider at capture time. PreviewViewModel runs location warmup while the preview is visible, gates it on the location setting, and pauses it during video recording. The app module binds LocationManagerLocationProvider and declares the location permissions.
…r-provider' into temcguir/location-pipeline-plumbing
…r-provider' into temcguir/location-pipeline-plumbing
…anager-di The LocationManager-backed LocationProvider binding previously lived in the app module's shared di package. Host applications that reuse that package would receive the hardware-backed binding unconditionally and could not substitute a fake or an alternative implementation. Mirror the low-light-playservices-di pattern: keep the optional contract in :core:location:location-di and move the concrete binding into a dedicated module that :app opts into explicitly.
Contributor
There was a problem hiding this comment.
Code Review
This pull request implements geographic location tagging (geotagging) for both image and video captures. It introduces location permissions, a new Hilt module for location provider dependency injection, location hardware warmup logic in the preview lifecycle, and updates to the camera session to attach location metadata to output files. The review feedback focuses on improving robustness by sanitizing coordinates to prevent out-of-bounds crashes, adopting idiomatic Kotlin patterns (such as using apply instead of complex .let blocks and smart-casting Optional values), and restricting dependency injection provider visibility to internal.
CameraX video OutputOptions.Builder.setLocation throws IllegalArgumentException when latitude is outside [-90, 90] or longitude is outside [-180, 180]. Drop out-of-range locations in CameraXCameraSystem before they reach image metadata or video output options, using a shared Location.takeIfInBounds() helper. This replaces the try/catch around ImageCapture.Metadata.setLocation, which does not validate and could not throw. Also pass the nullable location to OutputOptions builders directly and resolve the Optional LocationProvider once in PreviewViewModel.
…ir/location-pipeline-plumbing # Conflicts: # feature/preview/src/main/java/com/google/jetpackcamera/feature/preview/PreviewViewModel.kt # ui/controller/impl/src/main/java/com/google/jetpackcamera/ui/controller/impl/CaptureControllerImpl.kt # ui/controller/impl/src/test/java/com/google/jetpackcamera/ui/controller/impl/CaptureControllerImplTest.kt
…ir/location-pipeline-plumbing
…ir/location-pipeline-plumbing
…ir/location-pipeline-plumbing
startLocationWarmup()/stopLocationWarmup() become startLocationUpdates()/ stopLocationUpdates(), and updateLocationWarmup() becomes syncLocationUpdates(), matching LocationProvider.runLocationUpdates(). Test names updated to match. No behavior change.
…ir/location-pipeline-plumbing
Ethan-Kwok
approved these changes
Oct 5, 2026
- Treat an exception from getCurrentLocation() as no location so capture proceeds. - Catch failures from runLocationUpdates() and restart the updates job if it is no longer active. - Apply the initial location setting before the first settings emission. - Document on LocationProvider that implementations should not throw.
…ir/location-pipeline-plumbing
…ir/location-pipeline-plumbing
…ir/location-pipeline-plumbing
…ir/location-pipeline-plumbing
…ir/location-pipeline-plumbing
…r-provider' into temcguir/location-pipeline-plumbing
PreviewViewModel combines preview visibility, the location setting, and recording state into one flow and runs location updates with collectLatest, replacing the @volatile flag and manual job tracking. FakeCameraSystem stores capture locations in plain properties since it is only accessed from the test thread.
Assign the unused result to satisfy the CheckResult lint check.
…r-provider' into temcguir/location-pipeline-plumbing
Calling startLocationUpdates while a session is running keeps the current session instead of restarting it.
…ir/location-pipeline-plumbing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Wires the location stack into capture. Photos and videos are tagged with the device location when the user has enabled location saving and granted location permission.
This is Part 3 of the location series, stacked on #587.
How it works
While the preview is visible,
PreviewViewModelruns the location provider from #587, which keeps an in-memory cached location (see the diagram in #587 for how location is acquired). On shutter press, the capture is tagged with the cached location without waiting on location hardware.Key Changes
:core:camera:location: Location?parameter toCameraSystem.takePictureandCameraSystem.startVideoRecording(defaultnull).ImageCapture.Metadata. Out-of-range coordinates are dropped instead of failing the capture.FileOutputOptions,FileDescriptorOutputOptions,MediaStoreOutputOptions).:ui:controller:impl:CaptureControllerImplaccepts an optionalLocationProviderand readsgetCurrentLocation()at capture time for both images and videos.:feature:preview:PreviewViewModelinjectsOptional<LocationProvider>.LocationProvider.runLocationUpdates()while the preview is visible (startLocationUpdates()/stopLocationUpdates()fromPreviewScreen'sLifecycleStartEffect).locationEnabledsetting.:core:location:location-manager-di(new):LocationManagerLocationProvideras the concreteLocationProvider. The optional contract stays in:core:location:location-di, following thelow-light-playservices-dipattern, so host applications opt in explicitly and can substitute their own implementation.:app::core:location:location-manager-di.ACCESS_FINE_LOCATIONandACCESS_COARSE_LOCATION, and the location hardware features asrequired="false".PreviewViewModelTest: 9 tests covering location update start/stop, setting toggles, absent provider, video recording pause/resume, and capture with the setting disabled.CaptureControllerImplTest: 4 tests covering location passed to image and video capture, and omitted when disabled.PermissionsTest(instrumented): location permission page granted and denied paths.FakeCameraSystemrecords the last location passed to capture calls.