feat(profile): profile location — coarse capture, manual entry, and an honest non-clear (#37) - #130
Merged
Merged
Conversation
Adds the web's "Profile location" section to Android Settings: the optional latitude/longitude pair on the account, which the help centre documents as "Optional location for your profile, used by the Weather widget and similar location-aware features". It sits between Message settings and Permissions, the order /help/settings itself uses. Coordinates can be captured from the device or typed in: - Capture asks for ACCESS_COARSE_LOCATION only — the stored pair drives city-level surfaces, not navigation — and always shows the app's own rationale *before* the system dialog, since afterwards the decision has already been made. The reading is one-shot, taken only from a press of "Use my location", and rounded to about a kilometre before it is saved. - Refusing costs nothing. Any denial, including a permanent one, leaves manual entry and the rest of Settings working; the section says so, in plain text and not in the error colour. - Manual entry is validated against -90..90 and -180..180 and refused before any request is made. A saved location cannot be removed, and the UI says so instead of offering a button that would fail. PATCH /api/user/update validates `latitude` as a required number whenever the key is present, so a JSON null, an empty string and the string "null" are all answered with 400 "latitude must be a number between -90 and 90"; omitting the key leaves the value untouched, and 0,0 is a real position in the Gulf of Guinea rather than an absence. So "Clear location" is rendered disabled with the reason beside it, the view model can only build LocationUpdate.Set, and the rationale dialog says up front that a location can later be replaced but not removed. The three-state LocationUpdate / JsonElement plumbing stays, with the probe results recorded on LocationUpdate.Clear and its request shape pinned by tests, so re-enabling it when the API allows a null is a one-liner. This is an API gap, not an app limitation. Nothing is applied optimistically here: unlike a page size, a row claiming the account stores a position it does not would be a privacy claim the app cannot back up, so the displayed value is only ever what the server confirmed. Closes #37
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.
Closes #37. Part of epic #31 — the last of its six sub-issues.
The headline: you cannot clear a location, and the UI says so
The first implementation shipped a Clear button on the assumption that explicit nulls would unset the
coordinates. I probed the live API and they do not. Against the test account (values restored
afterwards and verified):
{"latitude":null,"longitude":null}latitude must be a number between -90 and 90{"latitude":"","longitude":""}{"latitude":"null","longitude":"null"}{"latitude":0,"longitude":0}0/0PATCH /api/user/updatevalidates the key as a required number whenever present, and omitting itmeans "leave unchanged".
0,0is accepted but that is Null Island — a real coordinate, not anabsence. So there is no way to unset a profile location through the API.
The fix makes the broken path unrepresentable rather than merely avoided:
clearLocation()isgone and the private
patch(update: LocationUpdate)becamesave(coordinates: Coordinates), whichbuilds
LocationUpdate.Setitself — the ViewModel literally cannot express a clear, so no user actionand no future careless edit can issue the request.
"Clear location" stays visible but disabled, with the reason immediately beneath it. Visible-but-
disabled was chosen over removing the control (because "how do I remove this?" is asked at the
button, and a missing control leaves that unanswered) and over enabled-explains-on-tap (which makes
someone press a control to learn it does nothing).
The
LocationUpdate.Clear/JsonElement?plumbing is kept, with the probe recorded verbatim inits KDoc — re-enabling is a one-liner the day the API accepts a null.
The rest
DeviceLocationSourceis the single component that can read a position; it usesthe framework
LocationManager(no Play Services) and reads onlyfused/network/passive—GPS is excluded because it needs
ACCESS_FINE_LOCATION, which the app never requests. One-shot,10 s budget, last-known fallback: no subscription, no background reader, nothing on load.
that; a typed coordinate is saved exactly as typed.
background, that it is rounded, that it is saved to the account, and that saying no is fine. It now
also states that a saved location can be replaced but not removed, which belongs in the consent
moment.
denial adds where the decision can be changed. Manual entry and everything else keep working.
does not is a privacy claim the app cannot back up, so the displayed value is always server truth.
Placement
/help/settingshas two relevant sections: Profile location ("Latitude / Longitude: Optionallocation for your profile…"), listed after Message settings and before Permissions; and
Permissions. The coordinates went into their own Profile location group in exactly the web's
position, and the permission request stays inside that group, attached to the button that needs
it — on Android the system dialog must follow the action that triggers it. #34's Permissions group is
untouched.
Privacy copy
The help centre does not say who can see a stored location, so nothing was guessed. The section
states: it is saved on the account (not just the device), the public profile does not include it, the
help centre does not say who else can, and it can be replaced but not removed. The public-profile
claim is live-verified — unauthenticated
GET /api/users/messengerreturns no coordinates.Worth flagging:
/help/settingssays of the web's geolocation grant that InterlinedList "does notstore your raw coordinates", which sits badly beside a permanently stored
47.6062 / -122.3321on theuser record. The app's copy describes what actually happens.
Verification
./gradlew :app:assembleDebug testDebugUnitTest→ BUILD SUCCESSFUL, 1411 tests, 0 failures.Includes
the API refuses a clear and the cached location survives it(replaying the exact 400 body),no user action can send a clear(exercises save, device capture, denial and dismiss, asserting noLocationUpdate.Clearever reaches the repository), permission-denied paths sending and readingnothing, and out-of-range entries refused with no request. Compose tests compile, not executed.
API gap to raise separately
PATCH /api/user/updatehas no way to unsetlatitude/longitude, so "never set" and "set once,now regretted" are not both reachable. The help centre calls it "Optional location", which is only
half true — optional to set, impossible to unset. Worth checking whether the web can clear it (a
different endpoint, or a direct DB write); if not, every web user is in the same position.