Allowing microphone access(permission) in WebView [Android Studio] [Java]

18,793

Solution 1

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

Example:

MainActivity

public class MainActivity extends AppCompatActivity {
    private static final int MY_PERMISSIONS_REQUEST_RECORD_AUDIO = 101;
    private ActivityMainBinding mBinding;
    private PermissionRequest myRequest;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mBinding = DataBindingUtil.setContentView(this, R.layout.activity_main);
        setWebView();
    }

    private void setWebView() {
        mBinding.webView.getSettings().setJavaScriptEnabled(true);
        mBinding.webView.getSettings().setJavaScriptCanOpenWindowsAutomatically(true);
        mBinding.webView.setWebViewClient(new WebViewClient());

        mBinding.webView.getSettings().setSaveFormData(true);
        mBinding.webView.getSettings().setSupportZoom(false);
        mBinding.webView.getSettings().setCacheMode(WebSettings.LOAD_NO_CACHE);
        mBinding.webViemBinding.webView.getSettings().setPluginState(WebSettings.PluginState.ON);


        mBinding.webView.setWebChromeClient(new WebChromeClient() {
            @Override
            public void onPermissionRequest(final PermissionRequest request) {
                myRequest = request;

                for (String permission : request.getResources()) {
                    switch (permission) {
                        case "android.webkit.resource.AUDIO_CAPTURE": {
                            askForPermission(request.getOrigin().toString(), Manifest.permission.RECORD_AUDIO, MY_PERMISSIONS_REQUEST_RECORD_AUDIO);
                            break;
                        }
                    }
                }
            }
        });

        mBinding.webView.loadUrl("<your url");
    }

    @Override
    public void onBackPressed() {
        if (mBinding.webView.canGoBack()) {
            mBinding.webView.goBack();
        } else {
            super.onBackPressed();
        }
    }

    @Override
    public void onRequestPermissionsResult(int requestCode,
                                           String permissions[], int[] grantResults) {
        switch (requestCode) {
            case MY_PERMISSIONS_REQUEST_RECORD_AUDIO: {
                Log.d("WebView", "PERMISSION FOR AUDIO");
                if (grantResults.length > 0
                        && grantResults[0] == PackageManager.PERMISSION_GRANTED) {


                    // permission was granted, yay! Do the
                    // contacts-related task you need to do.
                    myRequest.grant(myRequest.getResources());
                    mBinding.webView.loadUrl("<your url>");

                } else {

                    // permission denied, boo! Disable the
                    // functionality that depends on this permission.
                }
            }
            // other 'case' lines to check for other
            // permissions this app might request
        }
    }
    public void askForPermission(String origin, String permission, int requestCode) {
        Log.d("WebView", "inside askForPermission for" + origin + "with" + permission);

        if (ContextCompat.checkSelfPermission(getApplicationContext(),
                permission)
                != PackageManager.PERMISSION_GRANTED) {

            // Should we show an explanation?
            if (ActivityCompat.shouldShowRequestPermissionRationale(MainActivity.this,
                    permission)) {

                // Show an expanation to the user *asynchronously* -- don't block
                // this thread waiting for the user's response! After the user
                // sees the explanation, try again to request the permission.

            } else {

                // No explanation needed, we can request the permission.

                ActivityCompat.requestPermissions(MainActivity.this,
                        new String[]{permission},
                        requestCode);
            }
        } else {
            myRequest.grant(myRequest.getResources());
        }
    }

}

Mainfest.xml:

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.MICROPHONE" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-feature android:name="android.hardware.audio.low_latency" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-feature android:name="android.hardware.audio.pro" />
<uses-feature android:name="android.hardware.microphone"/>      

Build.gradle:

android {
    compileSdkVersion 26
    defaultConfig {
        applicationId "myapp.example.com.myapplication"
        minSdkVersion 21
        targetSdkVersion 21
        versionCode 1
        versionName "1.0"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }

Solution 2

Summarising, you need the following components:

  1. The MODIFY_AUDIO_SETTINGS permission.
  2. A WebChromeClient and listen to callbacks for onPermissionRequest
  3. Inside this callback, check for the the resources coming in the request object, create a global variable of this request object, iterate and look for specific permission inside the PermissionRequest class, ex. PermissionRequest.RESOURCE_VIDEO_CAPTURE which translates to the Manifest.permission.CAMERA permission, check if your app has this permission or not, using any mechanism you wish if not, request, if yes do 4.
  4. In the permission callback or if the permission is granted, use the request object to grant the permission ex. request.grant(new String[]{PermissionRequest.RESOURCE_VIDEO_CAPTURE}) and you're good to go.

snippet:

webView.webChromeClient = object: WebChromeClient(){
            override fun onPermissionRequest(request: PermissionRequest?) {
                super.onPermissionRequest(request)

                webkitPermissionRequest = request

                request?.resources?.forEach {
                    when(it){
                        PermissionRequest.RESOURCE_AUDIO_CAPTURE-> {
                            askForWebkitPermission(it, Manifest.permission.RECORD_AUDIO, REQUEST_CODE_PERMISSION_AUDIO)
                        }
                        PermissionRequest.RESOURCE_VIDEO_CAPTURE->{
                            askForWebkitPermission(it, Manifest.permission.CAMERA, REQUEST_CODE_PERMISSION_CAMERA)
                        }
                    }
                }
            }
        }

private fun askForWebkitPermission(webkitPermission: String, androidPermission: String, requestCode: Int){
        val context = activity?: return
        if (context.hasPermission(androidPermission)){
            webkitPermissionRequest!!.grant(arrayOf(webkitPermission))
        }else{
            requestPermissions(arrayOf(androidPermission), requestCode)
        }
    }

Hope this helps.

Google's sample for the same: https://github.com/googlesamples/android-PermissionRequest

Share:
18,793
Raynoro
Author by

Raynoro

Updated on July 26, 2022

Comments

  • Raynoro
    Raynoro almost 2 years

    I am currently working on a music tuner that uses html5 and a webview to display the 'application'. I've written the all the permission required in manifest and I think for webview there's another permission required.

    I am using this https://jbergknoff.github.io/guitar-tuner/ as a sample redirect page for now

    Here's my manifest.xml

    <?xml version="1.0" encoding="utf-8"?>
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.raynordev.projectrosin">
    
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.MICROPHONE" />
    <uses-permission android:name="android.permission.RECORD_AUDIO" />
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-feature android:name="android.hardware.audio.low_latency" />
    <uses-feature android:name="android.hardware.audio.pro" />
    <uses-feature android:name="android.hardware.microphone" android:required="true"/>
    
    
    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity
            android:name=".MainActivity"
            android:theme="@style/Theme.AppCompat.Light.NoActionBar">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
    
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity android:name="com.raynordev.projectrosin.HomeActivity">        
    
      </activity>
     </application>
    </manifest>
    

    Here's my .java

    public class HomeActivity extends AppCompatActivity {
    
    private WebView wv;
    private String TAG = "HomeActivity";
    private static final int REQUEST_INTERNET = 200;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_home);
        WebView wv = (WebView) findViewById(R.id.webView);
        wv.getSettings().setJavaScriptEnabled(true);
        wv.getSettings().setJavaScriptCanOpenWindowsAutomatically(true);
    
    
        wv.setWebViewClient(new WebViewClient());
        wv.setWebChromeClient(new WebChromeClient());
    
        wv.loadUrl("https://jbergknoff.github.io/guitar-tuner/");
     }
    }
    

    If require more information from me, please let me know.

    Thank you everyone!!

  • Ataur Rahman Munna
    Ataur Rahman Munna over 6 years
    Add some explanation to your answer.
  • Niall Murphy
    Niall Murphy over 6 years
    It might not have any explanation but it's the only the that worked for me.
  • Sundeep1501
    Sundeep1501 almost 6 years
    request.getResources() is not available below api level 19.Do we have any solution?
  • Yin Gong
    Yin Gong over 5 years
    <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.MICROPHONE" /> <uses-permission android:name="android.permission.RECORD_AUDIO" /> <uses-permission android:name="android.permission.INTERNET" /> <uses-feature android:name="android.hardware.audio.low_latency" /> <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" /> <uses-feature android:name="android.hardware.audio.pro" /> <uses-feature android:name="android.hardware.microphone"/> adding to Mainfest.xml is the key to use getUserMedia()
  • Tariq Mahmood
    Tariq Mahmood about 4 years
    private ActivityMainBinding mBinding; mBinding = DataBindingUtil.setContentView(this, R.layout.activity_main); Facing Error on these lines
  • David
    David over 2 years
    Why do I need to create a global variable for the request? Surely the request is passed "by reference" and could be used directly?
  • Javad B
    Javad B over 2 years
    This is the right answer, some permissions like this need to granted with "webkitPermissionRequest!!.grant(arrayOf(webkitPermission))" too