Add local network permission handling for private CalDAV servers - #146
Open
patrickunterwegs wants to merge 3 commits into
Open
Add local network permission handling for private CalDAV servers#146patrickunterwegs wants to merge 3 commits into
patrickunterwegs wants to merge 3 commits into
Conversation
Android 17 (API 37) gates local network access behind the android.permission.ACCESS_LOCAL_NETWORK runtime permission; below that it came for free with INTERNET. Since targetSdk is 37, adding a CalDAV account on a LAN address silently failed: denied TCP connects do not fail fast, they time out, so the app looked broken rather than blocked. Reproduced against Radicale on a phone where targetSdk 36 connects and 37 times out. Introduce the repo's first runtime-permission API, kept generic so further permissions only need a new AppPermission constant and a branch in the Android actual: - PermissionRequester (expect/actual) with status/request/openAppSettings, following the rememberX() shape used by ImagePicker. Android implements it for real; iOS can only open settings, since its own prompt is raised implicitly on first connection and cannot be queried; Desktop and Web report NOT_APPLICABLE. - isPrivateNetworkHost() classifies RFC1918, link-local, ULA, loopback, .local and single-label hosts, so people syncing with a hosted provider never see a "nearby devices" prompt. In the add-account sheet, both routes to OnAddPrincipal now funnel through one submit() that requests the permission first when the host is private, and the server field shows whether access is granted with a button to review it in system settings. Rediscovery and sync are deliberately not gated: they require an account added earlier, so that flow has already been through this. Also declare NSLocalNetworkUsageDescription in the three iOS Info.plists. iOS already prompts and works, but the key supplies the purpose string shown in that prompt and is effectively mandatory on iOS 18+. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019HgjhLvQ2D1St6oxS6nx8T
Requesting the local network permission now ends the tap: the account is not added behind the OS dialog, the user taps Add account again once they have answered it. Resuming for them meant holding the credentials in a second piece of state and deciding what to do on a refusal - and the people who reach this are pointing the app at a server on their own network, so tapping again is no burden. The permission callback only updates the indicator now. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019HgjhLvQ2D1St6oxS6nx8T
Adding an account now always runs discovery, permission or not, so both paths to OnAddPrincipal go back to exactly what they are on main and the sheet's behaviour change is just the notice and its button. Someone pointing the app at a LAN server can read the timeout and act on the notice sitting right above the button. The manage button now picks its action from the status: DENIED is the one state the OS may still prompt for, so it asks there; GRANTED can only be revoked in settings, and UNKNOWN is iOS, which has nothing to ask through, so both open settings. Android cannot tell "never asked" from "refused" - both read DENIED - so this prompts on the first-run case and does nothing visible after a permanent denial. Accepted to keep the sheet at one piece of permission state and no gate. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019HgjhLvQ2D1St6oxS6nx8T
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
Adds permission handling for accessing private/local network CalDAV servers on Android 17+ and iOS, with UI feedback during account setup. The app now detects when a user is connecting to a private network host and requests the necessary OS permission before attempting the connection.
Key Changes
Permission framework (
PermissionRequester.kt): New common interface for platform-specific permission handling withAppPermission.LOCAL_NETWORKenumandroid.permission.ACCESS_LOCAL_NETWORK(API 37+)UNKNOWNstatus (iOS raises consent implicitly on first connection)NOT_APPLICABLEPrivate network detection (
LocalNetworkAddress.kt): New utility to identify private/local hosts.localsuffix) and single-label hostnamesAccount setup UI (
AddPrincipalBottomSheet.kt):LocalNetworkPermissionNoticecomposable showing permission status with icon and messagesubmit()function that checks permission before dispatchingPlatform manifests: Added
NSLocalNetworkUsageDescriptionto iOS Info.plist files andACCESS_LOCAL_NETWORKpermission to Android manifests with explanatory commentsLocalization: Added four new strings for permission states (granted, denied, unknown, manage button)
Implementation Details
https://claude.ai/code/session_019HgjhLvQ2D1St6oxS6nx8T