camera: requestAudioPermission — in-app prompt first, settings on permanent denial

Banner-tap entry point that shows the system mic prompt when the OS
will still surface one, and deep-links to Settings only on permanent
denial. Fixes the fresh-install trap where the mic entry isn't in the
Privacy pane until requestAccess has fired at least once.

Android tracks the first-asked state in SharedPreferences because
shouldShowRequestPermissionRationale returns false in two
observationally identical states (never asked vs permanently denied).
The existing initialize() request path writes the flag too, so a
banner tap after a record-then-deny correctly routes to Settings.

Refactored Android pendingPermission into PendingPermission(primary,
kind, cb) so audio-only requests check RECORD_AUDIO results instead
of always checking CAMERA.
This commit is contained in:
agra
2026-05-21 08:50:39 +03:00
parent 1a7ce1ac1b
commit a508aca2bb
7 changed files with 369 additions and 242 deletions

View File

@@ -272,6 +272,27 @@ public class CameraPlugin: NSObject, NativePlugin, FlutterStreamHandler {
let granted = AVCaptureDevice.authorizationStatus(for: .audio) == .authorized
result(granted)
case "requestAudioPermission":
// On `.notDetermined` the system can still show the in-app
// prompt; on `.denied`/`.restricted` it can't and on a
// fresh install the mic entry isn't in the Privacy pane
// until requestAccess has fired at least once, so the
// settings fallback only makes sense after first prompt.
let status = AVCaptureDevice.authorizationStatus(for: .audio)
switch status {
case .authorized:
result(true)
case .notDetermined:
AVCaptureDevice.requestAccess(for: .audio) { granted in
DispatchQueue.main.async { result(granted) }
}
default:
DispatchQueue.main.async {
CameraSettings.openAppSettings()
result(false)
}
}
case "openSettings":
DispatchQueue.main.async {
// Per-platform helper: iOS opens app-specific Settings