hotwired / turbo-android

Android framework for making Turbo native apps
MIT License
423 stars 51 forks source link

WebRTC audio and video access #231

Closed chriskroon closed 2 years ago

chriskroon commented 2 years ago

Hey All,

I got the Android App almost working, the only problem is requesting access to the microphone and camera.

AndroidManifest.xml image

MainSessionNavHostFragment.kt image

It ask for permissions but it will still nog grand access, any clue what I'm doing wrong?

Error message Android Studio image

The JavaScript on my rails application gives a getUserMediaDevice error.

Cheers, Chris

ghiculescu commented 2 years ago

You need to request the permission from the OS, then call request.grant after that. When you request from the OS it will show a "do you want to let the app do X" style dialog.

I found these docs helpful (replace locations with whatever permission you need): https://developer.android.com/training/location/permissions

Also these: https://developer.android.com/training/permissions/requesting#java

chriskroon commented 2 years ago

Hey Gihculenscu, I know and I ask for permissions right? I don't get what I miss in my code 😢

ghiculescu commented 2 years ago

I don't see anywhere in your code where you ask for permissions.

chriskroon commented 2 years ago

Ok, after long searching the internet (nice!) I found out how to ask permissions, which seems to work.

The prompt will show up on my phone and the warning is gone except I got a different error, which seems to be an issue with Chromium. [ERROR:web_contents_delegate.cc(232)] WebContentsDelegate::CheckMediaAccessPermission: Not supported.

If I search this error I only find Cordova and Ionic forums, any change someone knows what this is about?

For everyone who want to know how I ask for permissions:

class MainActivity : AppCompatActivity(), TurboActivity {
    val CAMERA_RQ = 101
    val RECORD_AUDIO_RQ = 102

    override fun onCreate(savedInstanceState: Bundle?) {
        initRequestPermissions()
    }

    private fun initRequestPermissions(){
        checkForPermissions(android.Manifest.permission.CAMERA, "camera", CAMERA_RQ)
        checkForPermissions(android.Manifest.permission.RECORD_AUDIO, "record audio", RECORD_AUDIO_RQ)
    }

    override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray ) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults)
        fun innerCheck(name: String){
            if (grantResults.isEmpty() || grantResults[0] != PackageManager.PERMISSION_GRANTED){
                Toast.makeText(applicationContext, "$name permission refused", Toast.LENGTH_SHORT).show()
            }
        }
        when (requestCode){
            CAMERA_RQ -> innerCheck("camera")
            RECORD_AUDIO_RQ -> innerCheck("record audio")            
        }
    }

    private fun checkForPermissions(permission: String, name: String, requestCode: Int){
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M){
            when{
                ContextCompat.checkSelfPermission(applicationContext, permission) == PackageManager.PERMISSION_GRANTED -> {
                    // Toast.makeText(applicationContext, "$name permission granted", Toast.LENGTH_SHORT).show()
                }
                shouldShowRequestPermissionRationale(permission) -> showDialog(permission, name, requestCode)
                else -> ActivityCompat.requestPermissions(this, arrayOf(permission), requestCode)
            }
        }
    }

    private fun showDialog(permission: String, name: String, requestCode: Int){
        val builder = AlertDialog.Builder(this)

        builder.apply{
            setMessage("Permission to access your $name is required to use this app")
            setTitle("Permission required")
            setPositiveButton("Ok"){ dialog, which ->
                ActivityCompat.requestPermissions(this@MainActivity, arrayOf(permission), requestCode)
            }
        }
        val dialog = builder.create()
        dialog.show()
    }
}
ghiculescu commented 2 years ago

Glad to hear you got it working!

I got a different error, which seems to be an issue with Chromium. [ERROR:web_contents_delegate.cc(232)] WebContentsDelegate::CheckMediaAccessPermission: Not supported.

I haven't seen that error before, but it seems pretty common in Android webviews in general (ie. not specific to Turbo). See https://stackoverflow.com/questions/38917751/webrtc-error-inside-chromium-webview-checkmediaaccesspermission-not-supported for example.

pierreem commented 1 year ago

Actually, you need to set the webChromeClient after turbo has done it, so in a "onVisitCompleted" inside your TurboWebFragment for example.

override fun onVisitCompleted(location: String, completedOffline: Boolean) {
        super.onVisitCompleted(location, completedOffline)
        session.webView.webChromeClient = object : TurboWebChromeClient(session) {
            override fun onPermissionRequest(request: PermissionRequest) {
                request.grant(request.resources)
            }
        }
    }
jayohms commented 1 year ago

@pierreem, you shouldn't replace the WebView's chrome client directly. You can provide your own instance that the library will use by overriding the createWebChromeClient() function in your web fragment:

class WebFragment : TurboWebFragment() {

    // other custom code

    override fun createWebChromeClient(): TurboWebChromeClient {
        return MyWebViewChromeClient(session)
    }
}

class MyWebViewChromeClient(session: TurboSession) : TurboWebChromeClient(session) {
   // your overrides here
}