Sunday, November 22, 2015

Android Dev: Playing Audio in WebView

WebView Audio screenshot
We already know that in HTML5 there is <audio> tag to display an audio player and play the attached audio. If we use it in Android WebView, the audio player is displayed, but there is no audio played, seems like the file is missing. Here I write a solution for this issue. Keep reading.

First, we create the layout containing WebView.

res/layout/activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <WebView
        android:id="@+id/webView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_alignParentStart="true"
        android:layout_alignParentTop="true" />
</RelativeLayout>

We are using JavaScript interface technique to play the audio. So, these functions can be invoked in HTML file.

AudioInterface.java
package com.exellen.webviewaudio;

import android.content.Context;
import android.content.res.AssetFileDescriptor;
import android.media.MediaPlayer;
import android.webkit.JavascriptInterface;

import java.io.IOException;

public class AudioInterface {
    private static final int STATE_STOP = 0;
    private static final int STATE_PLAY = 1;
    private static final int STATE_PAUSE = 2;
    private Context context;
    private int state;
    private MediaPlayer mp;
    private String fileName;

    public AudioInterface(Context c) {
        context = c;
        mp = new MediaPlayer();
        fileName = "";
        state = STATE_STOP;
    }

    @JavascriptInterface
    public void play(String aud) {
        aud = "web/" + aud;
        if (state == STATE_PAUSE && fileName.equals(aud)) {
            try {
                mp.start();
                state = STATE_PLAY;
            } catch (IllegalStateException e) {
                e.printStackTrace();
            }
        } else {
            fileName = aud;
            try {
                AssetFileDescriptor afd = context.getAssets().openFd(fileName);
                mp.reset();
                mp.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength());
                afd.close();
                mp.prepare();
                mp.start();
                state = STATE_PLAY;
            } catch (IllegalArgumentException | IllegalStateException | IOException e) {
                e.printStackTrace();
            }
        }
    }

    @JavascriptInterface
    public void pause() {
        if (state == STATE_PLAY) {
            try {
                mp.pause();
                state = STATE_PAUSE;
            } catch (IllegalStateException e) {
                e.printStackTrace();
            }
        }
    }

    @JavascriptInterface
    public void stop() {
        if (state == STATE_PLAY || state == STATE_PAUSE) {
            try {
                mp.stop();
                state = STATE_STOP;
            } catch (IllegalStateException e) {
                e.printStackTrace();
            }
        }
    }
}

In the activity class, configure the WebView: enable JavaScript, add the JavaScript interface we have just created, and load HTML file.

MainActivity.java
package com.exellen.webviewaudio;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.webkit.WebView;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        WebView webViewContent = (WebView) findViewById(R.id.webView);
        webViewContent.getSettings().setJavaScriptEnabled(true);
        webViewContent.addJavascriptInterface(new AudioInterface(this), "AudioInterface");
        webViewContent.loadUrl("file:///android_asset/web/audio.html");
    }
}

Finally, it is the HTML file. It is using jQuery for simplifying JavaScript features. Here we are calling the JavaScript interface we have created above to control the audio player. Don't forget to put this file in asset directory.

assets/web/audio.html
<html>
<head>
    <title>Playing Audio in WebView</title>
    <script type='text/javascript' src='js/jquery-2.1.4.min.js'></script>
    <script type='text/javascript'>
        $(document).ready(function() {
            $(".play").click(function(event) {
                AudioInterface.play($(this).attr('href'));
                return false;
            });
            $(".pause").click(function(event) {
                AudioInterface.pause();
                return false;
            });
            $(".stop").click(function(event) {
                AudioInterface.stop();
                return false;
            });
        });
    </script>
</head>
<body>
<div style="text-align: center;">
    <a href="mp3/002.mp3" class="play"><img src="images/play.png"/></a>
    <a href="#" class="pause"><img src="images/pause.png"/></a>
    <a href="#" class="stop"><img src="images/stop.png"/></a>
</div>
</body>
</html>

Just for your reference, here is the Android file structure.
WebView Audio file structure