0

I created a web video chat app that needs permission to access the Camera, Microphone and Location before making a call. The app works fine in browsers like Chrome and Firefox. The issue is that I'm trying to integrate this web app into an Android app using Android WebView. Even after granting permissions for the Camera, Microphone and Location in the WebView, I'm getting an 'Access Denied' error when opening the web app. I've tried updating the AndroidManifest.xml file and using the permission_handler package, but the error persists. I'm new to Flutter and would appreciate any help in solving this issue.

Code Language: Flutter

pubspec.yaml webview_flutter: ^4.10.0 webview_flutter_android: ^4.0.1 webview_flutter_wkwebview: ^3.16.1 webview_flutter_web: ^0.2.3+3 permission_handler: ^11.3.1

Here is what I tried so far

AndroidManifest.xml

<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>

<uses-feature android:name="android.hardware.location.gps" />
<uses-feature android:name="android.hardware.location" />
<uses-feature
    android:name="android.hardware.camera"
    android:required="true" />
<activity>
  ...
  <intent-filter>
    <action android:name="android.intent.action.MAIN"/>
    <category android:name="android.intent.category.LAUNCHER"/>
  </intent-filter>
  <intent-filter android:label="Deep Link">
    <action android:name="android.intent.action.VIEW" />
    <category android:name="android.intent.category.DEFAULT" />
    <category android:name="android.intent.category.BROWSABLE" />
    <data android:host="profile" android:scheme="scan" />
  </intent-filter>
   <intent-filter android:label="App Links">
   <action android:name="android.intent.action.VIEW" />
   <category android:name="android.intent.category.DEFAULT" />
   <category android:name="android.intent.category.BROWSABLE" />
   <data android:host="profile" android:scheme="https" />
  </intent-filter>
</activity>
 <activity android:name="io.flutter.embedding.android.FlutterActivity">
 <meta-data android:name="android.webkit.WebView.EnableSafeBrowsing" android:value="true" />
</activity>

WebView.dart

import 'package:flutter/material.dart';
import 'package:vc/utils/app_state.dart';
import 'package:webview_flutter/webview_flutter.dart';
import 'package:vc/utils/WebViewController.dart';

class WebViewPage extends StatefulWidget {
  final String url;

  const WebViewPage({Key? key, required this.url}) : super(key: key);

  @override
  State<WebViewPage> createState() => _WebViewPageState();
}

class _WebViewPageState extends State<WebViewPage> {
  late WebViewController _controller;
  final String redirectUrl = "vc://thankyou";

  @override
  void initState() {
    super.initState();
    _initializeWebView();
  }

  Future<void> _initializeWebView() async {
    try {
      final controller = await CustomWebViewController.createController(
        url: widget.url,
        redirectUrl: redirectUrl,
        onRedirect: _handleRedirection,
      );
      setState(() {
        _controller = controller;
      });
    } catch (e) {
      debugPrint('Error initializing WebViewController: $e');
    }
  }

  void _handleRedirection(String url) {
    debugPrint("Redirecting to: $url");
    final uniqueID = AppState().uniqueID ?? '';
    Navigator.pop(context);
    if (uniqueID.isNotEmpty) {
      Navigator.pushNamed(context, '/thankyou', arguments: uniqueID);
    } else {
      Navigator.pushNamed(context, '/thankyou');
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text("CallPage"),
      ),
      body: _controller == null
          ? const Center(child: CircularProgressIndicator())
          : WebViewWidget(controller: _controller),
    );
  }
}

WebViewController.dart

import 'package:permission_handler/permission_handler.dart';
import 'package:webview_flutter/webview_flutter.dart';

class CustomWebViewController {
  static Future<WebViewController> createController({
    required String url,
    required String redirectUrl,
    required Function(String) onRedirect,
  }) async {
    await _requestCameraPermission();

    final controller = WebViewController();

    await controller.setJavaScriptMode(JavaScriptMode.unrestricted);
    await controller.setNavigationDelegate(
      NavigationDelegate(
        onPageStarted: (String currentUrl) {
          if (currentUrl.startsWith(redirectUrl)) {
            onRedirect(currentUrl);
          }
        },
      ),
    );
    await controller.loadRequest(Uri.parse(url));
    return controller;
  }

  static Future<void> _requestCameraPermission() async {
    final status = await Permission.camera.request();
    if (status.isGranted) {
      print('Camera permission granted');
    } else if (status.isPermanentlyDenied) {
      print('Camera permission permanently denied');
      await openAppSettings();
    } else {
      print('Camera permission denied');
    }
  }
}

1
  • What API level are you targeting and what is the API level of the phone you are testing on? Also, do you see dialogs requesting the permissions? Commented Nov 23, 2024 at 14:48

1 Answer 1

0

It seems like you've already done a lot of the right things for integrating camera, microphone, and location permissions with the WebView in your Flutter app, but there are a few key points to address. Here are some potential solutions and suggestions to fix the issue:

<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />

<uses-feature android:name="android.hardware.camera" android:required="true" />
<uses-feature android:name="android.hardware.microphone" android:required="true" />

You need to enable permission requests in the WebView settings and handle them using WebViewClient.

WebViewController.dart

import 'package:permission_handler/permission_handler.dart';
import 'package:webview_flutter/webview_flutter.dart';
import 'package:flutter/services.dart';

class CustomWebViewController {
  static Future<WebViewController> createController({
    required String url,
    required String redirectUrl,
    required Function(String) onRedirect,
  }) async {
    await _requestPermissions();

    final controller = WebViewController();

    await controller.setJavaScriptMode(JavaScriptMode.unrestricted);
    await controller.setNavigationDelegate(
      NavigationDelegate(
        onPageStarted: (String currentUrl) {
          if (currentUrl.startsWith(redirectUrl)) {
            onRedirect(currentUrl);
          }
        },
      ),
    );

    // Enable media permissions (camera, microphone, etc.) in WebView
    await controller.setMediaPlaybackRequiresUserGesture(false);
    await controller.setAllowUniversalAccessFromFileURLs(true);
    
    // Handle permission requests from the WebView
    await controller.setPermissionRequestHandler(
      (controller, request) async {
        // Automatically accept camera and microphone requests
        if (request.origin == 'https://your-web-app-url.com') {
          if (request.permissions.contains(Permission.camera)) {
            controller.grantPermission(Permission.camera);
          }
          if (request.permissions.contains(Permission.microphone)) {
            controller.grantPermission(Permission.microphone);
          }
        }
        return true;
      }
    );
    
    await controller.loadRequest(Uri.parse(url));
    return controller;
  }

  static Future<void> _requestPermissions() async {
    final statusCamera = await Permission.camera.request();
    final statusMicrophone = await Permission.microphone.request();
    final statusLocation = await Permission.location.request();
    
    if (statusCamera.isGranted && statusMicrophone.isGranted && statusLocation.isGranted) {
      print('Permissions granted');
    } else {
      print('Permissions denied');
      // Optionally, show a dialog to direct users to settings to manually grant permissions
    }
  }
}
Sign up to request clarification or add additional context in comments.

1 Comment

Thanks for the suggestion. I'll try this solution

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.