Search code examples
androidcordovahtml5-video

HTMLVideoElement won't autoplay on a Cordova Android App


I'm working on a Cordova app, which has a QR code reader. It works by using WebRTC API. I'm trying to get it to autoplay without require user interaction with the page. But it only works sometimes, most of the time it won't autoplay and shows ugly grey square with a play button.

Here's what was done so far.

The video element is muted, set to autoplay and play inline:

const videoElement = document.createElement('video');
videoElement.style.width = `${width}px`;
videoElement.muted = true;
videoElement.playsInline = true;
videoElement.autoplay = true;
return videoElement;

Once the video started, the play() method is executed after 1 second:

let Element = $('video')[0];
Element.autoplay = true;

setTimeout(() => { Element.play() }, 1000);

The Cordova WebView setting which requires user interaction is set to false.

public void onCreate(Bundle savedInstanceState)
{
    super.onCreate(savedInstanceState);
    super.init();
    
    // enable Cordova apps to be started in the background
    Bundle extras = getIntent().getExtras();
    if (extras != null && extras.getBoolean("cdvStartInBackground", false)) {
        moveTaskToBack(true);
    }
    
    // Set by <content src="index.html" /> in config.xml
    loadUrl(launchUrl);
    
    WebView webView = (WebView) appView.getEngine().getView();
    WebSettings webSettings = webView.getSettings();
    webSettings.setMediaPlaybackRequiresUserGesture(false); // <-- This is the important part
}

Nevertheless, the camera stream still doesn't play automatically once it's created.


Solution

  • After tons and tons attempts, googling and trying different JS camera libraries, I can simply tell that it's not possible to consistently autoplay videos in a Cordova app.

    However, there is still solution. But it's completely different approach.

    You have to simulate a screen tap. Luckily, it's very easy.

    1. Go to your platforms/android/app/src/main/java/your package/MainActivity.java.

    2. Add the following imports:

        import android.os.SystemClock;
        import android.view.InputDevice;
        import android.view.Menu;
        import android.view.MotionEvent;
        import android.webkit.JavascriptInterface;
        import android.webkit.WebChromeClient;
        import android.webkit.WebSettings;
        import android.webkit.WebView;
    
    1. In the onCreate() handler add the following:
        webView = (WebView) appView.getEngine().getView();
        WebSettings webSettings = webView.getSettings();
        webSettings.setMediaPlaybackRequiresUserGesture(false);
        webView.addJavascriptInterface(new JSInterface(), "AndroidApp");
    
    1. Add the following class as a nested class to the MainActivity class:
        class JSInterface
        {       
            @JavascriptInterface
            public void SimulateScreenTap()
            {
                MainActivity.this.runOnUiThread(new Runnable() {
                    @Override
                    public void run()
                    {
                        long delta = 100;
                        long downTime = SystemClock.uptimeMillis();
                        float x = webView.getLeft() + (webView.getWidth() / 2);
                        float y = webView.getTop() + (webView.getHeight() / 2);
        
                        MotionEvent tapDownEvent = MotionEvent.obtain(downTime, downTime + delta, MotionEvent.ACTION_DOWN, x, y, 0);
                        tapDownEvent.setSource(InputDevice.SOURCE_CLASS_POINTER);
        
                        MotionEvent tapUpEvent = MotionEvent.obtain(downTime, downTime + delta + 2, MotionEvent.ACTION_UP, x, y, 0);
                        tapUpEvent.setSource(InputDevice.SOURCE_CLASS_POINTER);
        
                        webView.dispatchTouchEvent(tapDownEvent);
                        webView.dispatchTouchEvent(tapUpEvent);
                    }
                });
            }
        }
    
    1. Now when you need to start your video, simply call AndroidApp.SimulateScreenTap().