10

App is based on webRTC vs websocket . Android studio 2.3.2 last version.

I already use:

https protocol , autoplay , android version 7.0 (min > android 5.0) . App works on all supported browsers only android webview generate error.

This is first lines of errors log in logcat (android studio last ver):

E/chromium: [ERROR:audio_manager_android.cc(264)] Unable to select audio device! E/cr_VideoCapture: allocate: manager.openCamera: SecurityException: validateConnectLocked:1112: Caller "com.testwebrtc.nikola.myapplication" cannot open camera "1" without camera permission at android.hardware.camera2.CameraManager.throwAsPublicException(CameraManager.java:628) at android.hardware.camera2.CameraManager.openCameraDeviceUserAsync(CameraManager.java:347) at android.hardware.camera2.CameraManager.openCamera(CameraManager.java:450) at org.chromium.media.VideoCaptureCamera2.startCapture(VideoCaptureCamera2.java:661)

Other variant for error:

[ERROR:web_contents_delegate.cc(199)] WebContentsDelegate::CheckMediaAccessPermission: Not supported.

This is error log from chrome/webview (from errorCallBack - getUserMedia ):

      An error occcurred [CODE NotAllowedError]

      other error variant: 

      trackstarterror

Android Code looks like:

package com.project.TEST.xxx;
import android.annotation.TargetApi;
import android.app.Activity;
import android.app.Fragment;
import android.app.FragmentManager;
import android.content.Context;
import android.os.Build;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.webkit.CookieManager;
import android.webkit.PermissionRequest;
import android.webkit.WebChromeClient;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {

    private static final String TAG = MainActivity.class.getSimpleName();


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        FragmentManager fragmentManager = getFragmentManager();
        fragmentManager.beginTransaction()
                .replace(R.id.container, PlaceholderFragment.newInstance(1) )
                .commit();

    }

    /**
     * A placeholder fragment containing a simple view.
     */
    public static class PlaceholderFragment extends Fragment {
        /**
         * The fragment argument representing the section number for this
         * fragment.
         */
        private static final String ARG_SECTION_NUMBER = "section_number";

        /**
         * Returns a new instance of this fragment for the given section
         * number.
         */
        public static PlaceholderFragment newInstance(int sectionNumber) {
            PlaceholderFragment fragment = new PlaceholderFragment();
            Bundle args = new Bundle();
            args.putInt(ARG_SECTION_NUMBER, sectionNumber);
            fragment.setArguments(args);
            return fragment;
        }

        private WebView mWebRTCWebView;

        public PlaceholderFragment() {
        }

        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container,
                                 Bundle savedInstanceState) {
            View rootView = inflater.inflate(R.layout.fragment_main, container, false);
            mWebRTCWebView = (WebView) rootView.findViewById(R.id.fragment_main_webview);

            setUpWebViewDefaults(mWebRTCWebView);

            mWebRTCWebView.loadUrl("https://example.com/");

            mWebRTCWebView.setWebChromeClient(new WebChromeClient() {

                @Override
                public void onPermissionRequest(final PermissionRequest request) {
                    Log.d(TAG, "onPermissionRequest");
                    getActivity().runOnUiThread(new Runnable() {
                        @TargetApi(Build.VERSION_CODES.LOLLIPOP)
                        @Override
                        public void run() {

                            request.grant(request.getResources());

                        }
                    });
                }

            });

            return rootView;
        }

        @Override
        public void onStop() {
            super.onStop();

            /**
             * When the application falls into the background we want to stop the media stream
             * such that the camera is free to use by other apps.
             */
            mWebRTCWebView.evaluateJavascript("if(window.localStream){window.localStream.stop();}", null);
        }

        /*
        @Override
        public void onAttach(Activity activity) {
            super.onAttach(activity);
            ((MainActivity) activity).onSectionAttached(
                    getArguments().getInt(ARG_SECTION_NUMBER));
        }
*/

        @Override
        public void onAttach(Context context) {
            super.onAttach(context);

            Activity a;

            if (context instanceof Activity){
                a=(Activity) context;

            }

        }

        /**
         * Convenience method to set some generic defaults for a
         * given WebView
         *
         * @param webView
         */
        @TargetApi(Build.VERSION_CODES.LOLLIPOP)
        private void setUpWebViewDefaults(WebView webView) {
            WebSettings settings = webView.getSettings();

            // Enable Javascript
            settings.setJavaScriptEnabled(true);

            // Use WideViewport and Zoom out if there is no viewport defined
            settings.setUseWideViewPort(true);
            settings.setLoadWithOverviewMode(true);

            // Enable pinch to zoom without the zoom buttons
            settings.setBuiltInZoomControls(true);

            // Allow use of Local Storage
            settings.setDomStorageEnabled(true);

            if(Build.VERSION.SDK_INT > Build.VERSION_CODES.HONEYCOMB) {
                // Hide the zoom controls for HONEYCOMB+
                settings.setDisplayZoomControls(false);
            }

            // Enable remote debugging via chrome://inspect
            if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
                WebView.setWebContentsDebuggingEnabled(true);
            }

            webView.setWebViewClient(new WebViewClient());

            // AppRTC requires third party cookies to work
            CookieManager cookieManager = CookieManager.getInstance();
            cookieManager.setAcceptThirdPartyCookies(mWebRTCWebView, true);
        }
    }


}

Build.gradle-project :

// Top-level build file where you can add configuration options common to all sub-projects/modules.

buildscript {
    repositories {
        jcenter()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:2.3.2'

        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
    }
}

allprojects {
    repositories {
        jcenter()
    }
}

task clean(type: Delete) {
    delete rootProject.buildDir
}

gradle - module:

apply plugin: 'com.android.application'

android {
    compileSdkVersion 25
    buildToolsVersion "25.0.3"
    defaultConfig {
        applicationId "com.project.test.xxx"
        minSdkVersion 21
        targetSdkVersion 25
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
        externalNativeBuild {
            cmake {
                cppFlags "-fexceptions"
            }
        }
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
    externalNativeBuild {
        cmake {
            path "CMakeLists.txt"
        }
    }
}

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
        exclude group: 'com.android.support', module: 'support-annotations'
    })
    compile 'com.android.support:appcompat-v7:25.3.1'
    compile 'com.android.support.constraint:constraint-layout:1.0.2'
    testCompile 'junit:junit:4.12'
}

Android Manifest.xml :

....
  <uses-permission android:name="android.permission.CAMERA" />
    <uses-permission android:name="android.permission.CAPTURE_SECURE_VIDEO_OUTPUT" />
    <uses-permission android:name="android.permission.CAPTURE_VIDEO_OUTPUT" />
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.RECORD_AUDIO" />

    <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
    <uses-permission android:name="android.permission.CAPTURE_AUDIO_OUTPUT" />
    <uses-feature android:name="android.hardware.camera.autofocus" android:required="true" />
    <uses-feature android:name="android.hardware.camera.front" android:required="true" />
    <uses-feature android:name="android.hardware.camera" android:required="true" />
    <uses-feature android:name="android.hardware.camera.level.full" android:required="true" />
    <uses-feature android:name="android.hardware.camera.capability.raw" android:required="true" />
    <uses-feature android:name="android.hardware.camera.any" android:required="true" />
    <uses-feature android:name="android.hardware.microphone" android:required="true" />
    <uses-feature android:name="android.hardware.camera2" android:required="true" />

...
Nikola Lukic
  • 4,001
  • 6
  • 44
  • 75
  • 1
    could you share the whole code related to `webView`? for instance in provided below can't find `setWebViewClient` call – Siarhei May 30 '17 at 19:54
  • I make copy , looks same... Something about target and other version setting , i am still pionir for android platform.... – Nikola Lukic Jun 01 '17 at 08:03
  • @user5599807 My best score for now : I got remote stream on webview , also getusermedia pass success . No local video preview still black screen.... – Nikola Lukic Jun 02 '17 at 07:57
  • hi, sorry i was overloaded last time. have you deal with id? – Siarhei Jun 17 '17 at 11:35
  • 2
    Hi. Just went into this today. Did you find a solution? :) thanks – VinceOPS Oct 12 '17 at 09:24
  • Actually no , i use copy project from google ... – Nikola Lukic Oct 12 '17 at 09:28
  • I works on other projects , in new android api we need to check security and implement handle listener for that request. Alert message with "allow" button. I must check this if i miss something about api versions... – Nikola Lukic Oct 13 '17 at 16:28

5 Answers5

11

Grant permissions in this way:

yourWebView.setWebChromeClient(new WebChromeClient() {
                @Override
                public void onPermissionRequest(PermissionRequest request) {
                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                        request.grant(request.getResources());
                    }
                }
            });
Amos
  • 2,222
  • 1
  • 26
  • 42
  • 1
    Excellent answer, this saves lot of work, Thanks :) @Amos – Gundu Bandgar Dec 27 '19 at 06:00
  • 1
    Because time ticking i will ascept this answer. It's posible to be right answer. In one moment (maybe 7.0) permisions was strict definition ( you need to use grant call for user request). – Nikola Lukic Dec 27 '19 at 23:20
  • 4
    From a documentation point of view, this is wrong. https://developer.android.com/reference/android/webkit/PermissionRequest To avoid unintentionally granting requests for new permissions, you should pass the specific permissions you intend to grant to grant(), and avoid writing code like this example: permissionRequest.grant(permissionRequest.getResources()) // This is wrong!!! – Dmitrijs Mar 25 '20 at 12:52
  • 1
    You can grant ressources for your expected domain only. By example : if (request.origin.toString() == "https://$httpServerDomain/") { request.grant(request.resources) } else { request.deny() } – G33k Labs Apr 20 '20 at 10:05
  • This will help - @Dmitrijs mentioned you can try [this](https://stackoverflow.com/a/61609282/9427138) method as per the documentation – Adarsh Vijayan P Apr 27 '23 at 11:47
5

The WebView requests permission from the App, but the App is not granted these permissions from the user.

You should invoke App's permission request to the user, as mentioned in this document.

Now I found that the permissions WebRTC requires is those:

onPermissionRequest: android.webkit.resource.VIDEO_CAPTURE
onPermissionRequest: android.webkit.resource.AUDIO_CAPTURE
g00glen00b
  • 41,995
  • 13
  • 95
  • 133
Cris Liao
  • 61
  • 1
  • 4
  • 2
    Welcome to Stack Overflow. When providing an answer, it is better to fully explain your solution, providing an example if possible. Linking to documentation is nice if used as a reference to backup your answer, but is not sufficient on its own. – Matt Dec 05 '17 at 10:27
  • I will try it , it is very possible – Nikola Lukic Dec 05 '17 at 10:31
3

Your app need request permission before use camera

 <uses-permission android:name="android.permission.CAMERA" />
 <uses-feature android:name="android.hardware.camera" />
 <uses-feature android:name="android.hardware.camera.autofocus" />
AniGame CN
  • 39
  • 1
1

You need these permissions to access Camera and Microphone

<uses-permission android:name="android.permission.CAMERA"/>
<uses-feature android:name="android.hardware.camera" android:required="true"/>
<uses-feature android:name="android.hardware.camera.autofocus"/>
<uses-permission android:name="android.permission.RECORD_AUDIO" />

// don't miss this one
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" /> 

Next you need to grant permissions to your webview, check this link for more details:

webView.setWebChromeClient(new WebChromeClient(){
        @Override
        public void onPermissionRequest(PermissionRequest request) {
            runOnUiThread(() -> {
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                    String[] PERMISSIONS = {
                            PermissionRequest.RESOURCE_AUDIO_CAPTURE,
                            PermissionRequest.RESOURCE_VIDEO_CAPTURE
                    };
                    request.grant(PERMISSIONS);
                }
            });
        }
    });

If audio playback is not working, use this:

webView.getSettings().setMediaPlaybackRequiresUserGesture(false);
Dharman
  • 30,962
  • 25
  • 85
  • 135
Muhammad Shuja
  • 642
  • 5
  • 18
1

its problems for reload the webview , after too much research , found this code and create this below code :-

 mwebView.setWebChromeClient(new WebChromeClient() {
    @Override
    public void onPermissionRequest(final PermissionRequest request) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            myRequest = request;
            for (String permission : request.getResources()) {
                if (permission.equals("android.webkit.resource.AUDIO_CAPTURE")) {
                    demandForPermission(request.getOrigin().toString(), Manifest.permission.RECORD_AUDIO, MY_PERMISSIONS_REQUEST_RECORD_AUDIO);
                } else {
                    myRequest.grant(request.getResources());
                }
            }
        }
    }

    @Override
    public void onPermissionRequestCanceled(PermissionRequest request) {
        super.onPermissionRequestCanceled(request);
    }

more info in this page Android Webview

  • Always is good to get more android devices in working regime. thks for LOLLIPOP check. And welcome to the community! – Nikola Lukic Mar 18 '22 at 15:10