Skip to content

Trip planner: map-overlay layout (top-sheet form + bottom-sheet directions)#1872

Merged
bmander merged 4 commits into
mainfrom
feature/trip-plan-map-overlay
Jul 18, 2026
Merged

Trip planner: map-overlay layout (top-sheet form + bottom-sheet directions)#1872
bmander merged 4 commits into
mainfrom
feature/trip-plan-map-overlay

Conversation

@bmander

@bmander bmander commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

What

Restructures the Plan a trip flow so the directions map is the constant backdrop of the whole flow, instead of the form being a separate full screen.

  • Form → top sheet. The from/to form now slides down over the map from the top. It drags like a bottom sheet: an AnchoredDraggable state drives its height, and the full form cross-fades with a compact From → To summary bar as you minimize/maximize (drag the bottom handle, or tap it). Auto-collapses to the summary once a plan completes.
  • Directions → bottom sheet. A custom AnchoredDraggable sheet whose drag gesture lives on the handle only, so the directions LazyColumn scrolls freely inside. Opens to 40% of the screen, collapses to just the handle, seated above the nav-bar chrome while its surface still reaches the bottom edge (last row cleared via list scroll content padding).
  • Map is always present. DirectionsMapViewModel is mounted for the whole flow (base map before a plan, the drawn itinerary after). The standalone trip-plan toolbar is folded into the top sheet's header (back + "report problem").

Notes

  • TripPlanTopBar and the BottomSheetScaffold usage in the trip-plan container are removed; TripResultsSheet / TripResultsMap are reused unchanged aside from a new optional list bottom-inset.
  • Presentation-only change; no ViewModel/data changes.
  • Filed as a draft — still wants a broader on-device pass (both handsets/gesture vs. 3-button nav) and design eyes on the collapsed-height / cross-fade feel.

Testing

  • Builds: ./gradlew installObaGoogleDebug (verified on a Pixel 7 Pro).
  • Exercised on device: open Plan a trip → form slides down over the map; entering from/to computes a plan → form collapses to the summary bar, directions open in the bottom sheet, itinerary draws on the map; handle drag/tap on both sheets; back walks directions → form → results → exit.

Rebased continuation of #1683 (moved from the fork branch to a branch on this repo; rebased onto latest main). The map-overlay commit was replayed onto current main — which meanwhile refactored the same screen (region/report email now via TripPlanViewModel.otpContactEmail, locationOf/mapPickerCenter(), and a required params arg on TripResultsSheet); those were reconciled and the strict warnings-as-errors compile passes.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features
    • Trip planning and results now live in a dedicated “Directions” mode on the Home map, including from/to selection, map picking, itinerary display, and a results sheet.
    • Directions mode persists when reopening after trip-plan notifications.
  • UI Improvements
    • Streamlined endpoint controls (reverse/advanced/settings as icons), updated endpoint labels, and removed the “contacts” shortcut.
    • Results sheet spacing now accounts for navigation controls.
  • Bug Fixes
    • Trip-plan notifications now return users to the correct Home directions experience.

@coderabbitai

coderabbitai Bot commented Jul 15, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 6cc9a792-f87b-4530-abdf-494a7e2b6709

📥 Commits

Reviewing files that changed from the base of the PR and between 4ebf15a and 9b460b6.

📒 Files selected for processing (3)
  • onebusaway-android/src/main/java/org/onebusaway/android/directions/realtime/TripPlanMonitorService.kt
  • onebusaway-android/src/main/java/org/onebusaway/android/ui/home/HomeScreen.kt
  • onebusaway-android/src/main/java/org/onebusaway/android/ui/home/directions/DirectionsFeature.kt
🚧 Files skipped from review as they are similar to previous changes (3)
  • onebusaway-android/src/main/java/org/onebusaway/android/ui/home/directions/DirectionsFeature.kt
  • onebusaway-android/src/main/java/org/onebusaway/android/directions/realtime/TripPlanMonitorService.kt
  • onebusaway-android/src/main/java/org/onebusaway/android/ui/home/HomeScreen.kt

📝 Walkthrough

Walkthrough

Trip planning now runs as directions mode on the HOME map. HOME hosts planner and results state, renders endpoint picking and itinerary UI, draws itineraries through map directives, persists directions focus, and removes the standalone trip-plan destination.

Changes

HOME directions trip-planning flow

Layer / File(s) Summary
Map directions state and commands
onebusaway-android/src/main/java/org/onebusaway/android/map/MapViewModel.kt, onebusaway-android/src/main/java/org/onebusaway/android/ui/home/HomeViewModel.kt, onebusaway-android/src/main/java/org/onebusaway/android/ui/home/CurrentFocus*, onebusaway-android/src/main/java/org/onebusaway/android/ui/home/map/MapFeature.kt
Directions focus, itinerary rendering, clearing, teardown, persistence, and map directives are handled by the HOME map.
HOME directions UI and wiring
onebusaway-android/src/main/java/org/onebusaway/android/ui/HomeActivity.kt, onebusaway-android/src/main/java/org/onebusaway/android/ui/home/HomeNavHost.kt, onebusaway-android/src/main/java/org/onebusaway/android/ui/home/HomeScreen.kt, onebusaway-android/src/main/java/org/onebusaway/android/ui/home/directions/DirectionsFeature.kt
HOME receives planner state, enters directions mode from the menu, renders forms/results, supports map endpoint picking, and exposes advanced settings.
Trip planner and results surface
onebusaway-android/src/main/java/org/onebusaway/android/ui/tripplan/*, onebusaway-android/src/main/java/org/onebusaway/android/ui/tripresults/TripResultsScreen.kt, onebusaway-android/src/main/res/values/strings.xml
The standalone trip-plan screen is removed, contacts actions are removed from the form, and results delegate itinerary display to HOME.
Notification HOME restoration
onebusaway-android/src/main/java/org/onebusaway/android/directions/realtime/TripPlanMonitorService.kt
Monitoring notification intents no longer target the removed standalone trip-plan route.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant HomeScreen
  participant TripPlanViewModel
  participant TripResultsViewModel
  participant HomeViewModel
  participant MapViewModel
  User->>HomeScreen: Enter directions mode
  HomeScreen->>TripPlanViewModel: Set endpoints and planning options
  TripPlanViewModel->>TripResultsViewModel: Produce itineraries
  HomeScreen->>HomeViewModel: Show selected itinerary
  HomeViewModel->>MapViewModel: Dispatch itinerary map directive
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 74.19% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: the trip planner now uses a map-overlay layout with a top-sheet form and bottom-sheet directions.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/trip-plan-map-overlay

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

bmander added 2 commits July 16, 2026 23:15
…tions)

Restructures the "Plan a trip" flow so the directions map is the constant
backdrop instead of the form being a full screen:

- The form is now a top sheet that slides down over the map. It drags like a
  bottom sheet: an AnchoredDraggable state drives its height, and the full form
  cross-fades with a compact "From -> To" summary bar as it minimizes/maximizes.
- The directions are a custom bottom sheet whose drag gesture lives on the
  handle only, so the directions list scrolls freely inside. It opens to 40% of
  the screen and collapses to the handle, seated above the nav-bar chrome while
  its surface reaches the bottom edge (list clearance via scroll content
  padding).
- The map (DirectionsMapViewModel) is mounted for the whole flow and draws the
  itinerary once a plan completes; the standalone toolbar is folded into the
  sheet header.

TripPlanTopBar/BottomSheetScaffold usage removed; TripResultsSheet/Map reused.
Fold the trip planner into the home map instead of a separate screen, driven
by a new CurrentFocus.Directions in the existing focus system:

- CurrentFocus.Directions (persisted); HomeViewModel.enterDirections/exitDirections
  + MapDirective.ShowItinerary/ClearItinerary bridged in MapFeature.
- MapViewModel gains a DirectionsMapController session integrated with
  leaveCurrentView (entering directions drops stop/route focus; exiting restores
  nearby stops).
- The top-chrome search field is replaced by a compact trip-plan form while
  planning (DirectionsFormCard); a directions results sheet + itinerary render
  over the map; a plan error / no-route / loading state is now surfaced.
- Pick a From/To point on the home map (crosshair + confirm); current-location,
  date/time, and advanced-settings are wired.
- Retire the standalone TRIP_PLAN screen (TripPlanScreen/TripPlanFormSheet/
  DirectionsMapViewModel/TripResultsMap); the drawer entry now enters directions
  focus and the trip-monitor notification opens Home.
- Remove the address-book (contacts) picker (#1936 tracks place-intent receiving).

TripPlanViewModel/TripResultsViewModel are hosted at HomeActivity scope.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
onebusaway-android/src/main/java/org/onebusaway/android/directions/realtime/TripPlanMonitorService.kt (1)

232-244: 🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift

Implement directions restoration before removing the trip-plan route.

The notification carries the request and updated itineraries, but no supplied HOME path consumes them; the comment confirms restoration is deferred. Tapping a trip-change alert therefore opens generic HOME instead of the changed trip. Restore these extras into directions focus before shipping this removal.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@onebusaway-android/src/main/java/org/onebusaway/android/directions/realtime/TripPlanMonitorService.kt`
around lines 232 - 244, Update the notification tap flow around requestExtras
and openIntent so the serialized request and itineraries are restored into the
directions screen before removing the trip-plan route. Use the existing
directions restoration/navigation path, including the appropriate navigation
route extra if required, instead of opening generic HOME without
EXTRA_NAV_ROUTE; preserve the notification source metadata.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@onebusaway-android/src/main/java/org/onebusaway/android/ui/home/directions/DirectionsFeature.kt`:
- Around line 154-160: Update setCurrentLocation so a null lastKnownLocation()
is not always reported as a permission failure. Check the current
location-permission state first and show the permission message only when
permission is denied; otherwise show the appropriate location-unavailable
message before returning.
- Around line 165-203: Update pickTripDate and pickTripTime to perform date/time
reads, mutations, and writes using WallTime at the ViewModel/UI boundary rather
than raw epoch Long values or Calendar arithmetic. Convert WallTime to epoch
milliseconds only when supplying MaterialDatePicker or MaterialTimePicker
values, and wrap picker results back into WallTime before calling
viewModel.setDateTime.

In
`@onebusaway-android/src/main/java/org/onebusaway/android/ui/home/HomeScreen.kt`:
- Around line 694-702: Update the onConfirm handler for the DirectionsPickTarget
picker so it does not clear pickTarget when mapViewModel.camera.value or its
center is unavailable. Either disable confirmation until a camera center exists
or dismiss the picker only after successfully calling setFrom or setTo, while
preserving the existing endpoint selection behavior.

---

Outside diff comments:
In
`@onebusaway-android/src/main/java/org/onebusaway/android/directions/realtime/TripPlanMonitorService.kt`:
- Around line 232-244: Update the notification tap flow around requestExtras and
openIntent so the serialized request and itineraries are restored into the
directions screen before removing the trip-plan route. Use the existing
directions restoration/navigation path, including the appropriate navigation
route extra if required, instead of opening generic HOME without
EXTRA_NAV_ROUTE; preserve the notification source metadata.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 6ae0230d-1ae7-4bba-8059-64e5f4ced48b

📥 Commits

Reviewing files that changed from the base of the PR and between c758ef7 and 4ebf15a.

📒 Files selected for processing (18)
  • onebusaway-android/src/main/java/org/onebusaway/android/directions/realtime/TripPlanMonitorService.kt
  • onebusaway-android/src/main/java/org/onebusaway/android/map/DirectionsMapViewModel.kt
  • onebusaway-android/src/main/java/org/onebusaway/android/map/MapViewModel.kt
  • onebusaway-android/src/main/java/org/onebusaway/android/ui/HomeActivity.kt
  • onebusaway-android/src/main/java/org/onebusaway/android/ui/home/CurrentFocus.kt
  • onebusaway-android/src/main/java/org/onebusaway/android/ui/home/CurrentFocusPersistence.kt
  • onebusaway-android/src/main/java/org/onebusaway/android/ui/home/HomeNavHost.kt
  • onebusaway-android/src/main/java/org/onebusaway/android/ui/home/HomeScreen.kt
  • onebusaway-android/src/main/java/org/onebusaway/android/ui/home/HomeSheetLogic.kt
  • onebusaway-android/src/main/java/org/onebusaway/android/ui/home/HomeViewModel.kt
  • onebusaway-android/src/main/java/org/onebusaway/android/ui/home/directions/DirectionsFeature.kt
  • onebusaway-android/src/main/java/org/onebusaway/android/ui/home/map/MapFeature.kt
  • onebusaway-android/src/main/java/org/onebusaway/android/ui/tripplan/TripPlanDestinations.kt
  • onebusaway-android/src/main/java/org/onebusaway/android/ui/tripplan/TripPlanForm.kt
  • onebusaway-android/src/main/java/org/onebusaway/android/ui/tripplan/TripPlanScreen.kt
  • onebusaway-android/src/main/java/org/onebusaway/android/ui/tripresults/TripResultsScreen.kt
  • onebusaway-android/src/main/res/drawable/baseline_import_contacts_24.xml
  • onebusaway-android/src/main/res/values/strings.xml
💤 Files with no reviewable changes (4)
  • onebusaway-android/src/main/java/org/onebusaway/android/map/DirectionsMapViewModel.kt
  • onebusaway-android/src/main/java/org/onebusaway/android/ui/tripplan/TripPlanScreen.kt
  • onebusaway-android/src/main/res/drawable/baseline_import_contacts_24.xml
  • onebusaway-android/src/main/res/values/strings.xml

Comment on lines +165 to +203
private fun pickTripDate(activity: AppCompatActivity, viewModel: TripPlanViewModel) {
val current = viewModel.formState.value.dateTimeMillis
val picker = MaterialDatePicker.Builder.datePicker()
.setTitleText(R.string.trip_plan_date)
.setSelection(current)
.build()
picker.addOnPositiveButtonClickListener { selection ->
// Read the selection in UTC, exactly as the user saw it (matches the legacy form).
val utc = Calendar.getInstance(TimeZone.getTimeZone("UTC")).apply { timeInMillis = selection }
val calendar = Calendar.getInstance().apply {
timeInMillis = current
set(Calendar.YEAR, utc.get(Calendar.YEAR))
set(Calendar.MONTH, utc.get(Calendar.MONTH))
set(Calendar.DAY_OF_MONTH, utc.get(Calendar.DAY_OF_MONTH))
}
viewModel.setDateTime(calendar.timeInMillis)
}
picker.show(activity.supportFragmentManager, "DATE_PICKER")
}

private fun pickTripTime(activity: AppCompatActivity, viewModel: TripPlanViewModel) {
val current = viewModel.formState.value.dateTimeMillis
val calendar = Calendar.getInstance().apply { timeInMillis = current }
val timeFormat =
if (DateFormat.is24HourFormat(activity)) TimeFormat.CLOCK_24H else TimeFormat.CLOCK_12H
val picker = MaterialTimePicker.Builder()
.setTimeFormat(timeFormat)
.setHour(calendar.get(Calendar.HOUR_OF_DAY))
.setMinute(calendar.get(Calendar.MINUTE))
.setTitleText(R.string.trip_plan_time)
.setTheme(R.style.ThemeOverlay_App_TimePicker)
.build()
picker.addOnPositiveButtonClickListener {
calendar.set(Calendar.HOUR_OF_DAY, picker.hour)
calendar.set(Calendar.MINUTE, picker.minute)
viewModel.setDateTime(calendar.timeInMillis)
}
picker.show(activity.supportFragmentManager, "TIME_PICKER")
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift

Keep trip date/time arithmetic in the WallTime domain.

These helpers read, mutate, and write raw epoch Long values through Calendar. Use WallTime at the ViewModel/UI boundary and unwrap only for the Material picker APIs.

As per coding guidelines, “Use ServerTime, WallTime, and ElapsedTime from org.onebusaway.android.time for new time arithmetic instead of raw Long values.”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@onebusaway-android/src/main/java/org/onebusaway/android/ui/home/directions/DirectionsFeature.kt`
around lines 165 - 203, Update pickTripDate and pickTripTime to perform
date/time reads, mutations, and writes using WallTime at the ViewModel/UI
boundary rather than raw epoch Long values or Calendar arithmetic. Convert
WallTime to epoch milliseconds only when supplying MaterialDatePicker or
MaterialTimePicker values, and wrap picker results back into WallTime before
calling viewModel.setDateTime.

Source: Coding guidelines

Comment thread onebusaway-android/src/main/java/org/onebusaway/android/ui/home/HomeScreen.kt Outdated
- setCurrentLocation: a null last-known location no longer always reports
  "no location permission". Check the permission state first via
  PermissionUtils.hasGrantedAtLeastOnePermission; show the recoverable
  "trying to get a fix" message when permission is granted but there's no
  fix yet, and the permission message only when it's actually denied.
- DirectionsPickOverlay onConfirm: only clear pickTarget (dismiss the
  picker) after successfully committing an endpoint. With no map center
  available, keep the picker open instead of silently losing the selection.
@bmander
bmander merged commit 91591c3 into main Jul 18, 2026
3 checks passed
@bmander
bmander deleted the feature/trip-plan-map-overlay branch July 18, 2026 04:45
bmander added a commit that referenced this pull request Jul 18, 2026
#1940)

PR #1872 embedded directions into the home map and retired the standalone
TripPlanScreen, whose maybeRestoreFromIntent was the only consumer of the
trip-plan monitor's "your trip changed" notification intent. With that gone,
tapping the alert opened generic HOME instead of the changed trip: the intent
still carried the request bundle (copyIntoBundleSimple), the updated itineraries
(OTPConstants.ITINERARIES, JSON), and INTENT_SOURCE=NOTIFICATION, but nothing in
HOME read them.

Re-add the consumer in HomeActivity's launch-intent side-effect path, driving
the Activity-scoped VMs instead of the retired screen's: guard on
INTENT_SOURCE=NOTIFICATION, JSON-decode the itineraries, rehydrate the request
via TripRequestBuilder.initFromBundleSimple, then enterDirections() +
TripPlanViewModel.restoreFrom(...) to inject the precomputed itineraries as
PlanResult.Success without re-planning. The existing directions render path
(results sheet + map draw) reacts with no further changes; the producer side and
restoreFrom were already intact.

No re-restore guard is needed: the launch-intent channel submits each real
intent exactly once (cold onCreate seed, warm onNewIntent), so a config change
doesn't re-fire it. params=null (monitor not re-armed from the restore) matches
pre-#1872 behavior.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant