Two gates, and most people open one
A web page asking for the camera inside an Android app must pass two separate checks:
- The OS permission —
CAMERAdeclared in the manifest and granted by the user at runtime. Without it the app has no camera at all. - The WebView permission — even with OS permission, the WebView asks your code through
onPermissionRequestwhether this page may use it. The default answer is no.
Skip the second and the symptom is baffling: permission granted, getUserMedia() called, nothing happens — no prompt, no dialog, a rejected promise. It is the most common WebView support question, and why the generated code always includes the client alongside the manifest lines.
Request the minimum
Each declared permission appears on your listing, in the install flow and in the Data Safety form, and location in particular invites extra review. A permission you declare and never use makes the listing scarier for nothing. Declare what the pages actually call.
| Web API | Manifest permission | WebView hook |
|---|---|---|
navigator.geolocation | ACCESS_FINE_LOCATION | onGeolocationPermissionsShowPrompt |
getUserMedia({video}) | CAMERA | onPermissionRequest |
getUserMedia({audio}) | RECORD_AUDIO | onPermissionRequest |
<input type="file"> | none on modern Android | onShowFileChooser |
| Downloads | none on Android 10+ | setDownloadListener |
| Notifications | POST_NOTIFICATIONS (13+) | a messaging SDK |
Settings that shape how the site feels
javaScriptEnabled— off by default in a raw WebView. Nearly every site needs it on.domStorageEnabled— also off by default; without itlocalStoragethrows and sessions and carts silently break.useWideViewPort/loadWithOverviewMode— honour your responsive viewport rather than assuming desktop width.- Zoom — off feels more like an app; on is better for accessibility. Defensible either way if the layout is genuinely responsive.
- External links — without
shouldOverrideUrlLoading, a link to another site loads inside your chrome-less app and strands the user there.
File upload needs real code
An <input type="file"> in a bare WebView is inert — tapping it does nothing because the WebView has no picker. You implement onShowFileChooser, launch the intent and pass the chosen URIs back. Forgetting to call the callback when the picker is cancelled leaves the input dead until the page reloads: the WebView is still waiting for an answer.
Cleartext traffic
Plain http:// is blocked since Android 9 unless you opt in. Leave the block on and fix the site instead. The one legitimate exception is bundled content served from http://localhost inside the app, which is why the offline option adjusts that setting.