diff options
author | Ben Murdoch <benm@google.com> | 2010-10-18 10:15:55 -0700 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2010-10-18 10:15:55 -0700 |
commit | 83366dfb96ad15e945a90270d329ce19dcf6b7e3 (patch) | |
tree | 504fd5185229e990f813755a847f0d5969f3df99 | |
parent | e47150e933e6f610546f57183477f324566e521e (diff) | |
parent | 3d09e139b34bd4ed6a6836bb737bd2dc31d28c37 (diff) | |
download | frameworks_base-83366dfb96ad15e945a90270d329ce19dcf6b7e3.zip frameworks_base-83366dfb96ad15e945a90270d329ce19dcf6b7e3.tar.gz frameworks_base-83366dfb96ad15e945a90270d329ce19dcf6b7e3.tar.bz2 |
Merge "Add the HTML5Audio class, to support the audio tag." into gingerbread
-rw-r--r-- | core/java/android/webkit/HTML5Audio.java | 224 |
1 files changed, 224 insertions, 0 deletions
diff --git a/core/java/android/webkit/HTML5Audio.java b/core/java/android/webkit/HTML5Audio.java new file mode 100644 index 0000000..d292881 --- /dev/null +++ b/core/java/android/webkit/HTML5Audio.java @@ -0,0 +1,224 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.webkit; + +import android.media.MediaPlayer; +import android.media.MediaPlayer.OnBufferingUpdateListener; +import android.media.MediaPlayer.OnCompletionListener; +import android.media.MediaPlayer.OnErrorListener; +import android.media.MediaPlayer.OnPreparedListener; +import android.media.MediaPlayer.OnSeekCompleteListener; +import android.os.Handler; +import android.os.Message; +import android.util.Log; + +import java.io.IOException; +import java.util.Timer; +import java.util.TimerTask; + +/** + * <p>HTML5 support class for Audio. + */ +class HTML5Audio extends Handler + implements MediaPlayer.OnBufferingUpdateListener, + MediaPlayer.OnCompletionListener, + MediaPlayer.OnErrorListener, + MediaPlayer.OnPreparedListener, + MediaPlayer.OnSeekCompleteListener { + // Logging tag. + private static final String LOGTAG = "HTML5Audio"; + + private MediaPlayer mMediaPlayer; + + // The C++ MediaPlayerPrivateAndroid object. + private int mNativePointer; + + private static int IDLE = 0; + private static int INITIALIZED = 1; + private static int PREPARED = 2; + private static int STARTED = 4; + private static int COMPLETE = 5; + private static int PAUSED = 6; + private static int STOPPED = -2; + private static int ERROR = -1; + + private int mState = IDLE; + + private String mUrl; + private boolean mAskToPlay = false; + + // Timer thread -> UI thread + private static final int TIMEUPDATE = 100; + + // The spec says the timer should fire every 250 ms or less. + private static final int TIMEUPDATE_PERIOD = 250; // ms + // The timer for timeupate events. + // See http://www.whatwg.org/specs/web-apps/current-work/#event-media-timeupdate + private Timer mTimer; + private final class TimeupdateTask extends TimerTask { + public void run() { + HTML5Audio.this.obtainMessage(TIMEUPDATE).sendToTarget(); + } + } + + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case TIMEUPDATE: { + try { + if (mState != ERROR && mMediaPlayer.isPlaying()) { + int position = mMediaPlayer.getCurrentPosition(); + nativeOnTimeupdate(position, mNativePointer); + } + } catch (IllegalStateException e) { + mState = ERROR; + } + } + } + } + + // event listeners for MediaPlayer + // Those are called from the same thread we created the MediaPlayer + // (i.e. the webviewcore thread here) + + // MediaPlayer.OnBufferingUpdateListener + public void onBufferingUpdate(MediaPlayer mp, int percent) { + nativeOnBuffering(percent, mNativePointer); + } + + // MediaPlayer.OnCompletionListener; + public void onCompletion(MediaPlayer mp) { + resetMediaPlayer(); + mState = IDLE; + nativeOnEnded(mNativePointer); + } + + // MediaPlayer.OnErrorListener + public boolean onError(MediaPlayer mp, int what, int extra) { + mState = ERROR; + resetMediaPlayer(); + mState = IDLE; + return false; + } + + // MediaPlayer.OnPreparedListener + public void onPrepared(MediaPlayer mp) { + mState = PREPARED; + if (mTimer != null) { + mTimer.schedule(new TimeupdateTask(), + TIMEUPDATE_PERIOD, TIMEUPDATE_PERIOD); + } + nativeOnPrepared(mp.getDuration(), 0, 0, mNativePointer); + if (mAskToPlay) { + mAskToPlay = false; + play(); + } + } + + // MediaPlayer.OnSeekCompleteListener + public void onSeekComplete(MediaPlayer mp) { + nativeOnTimeupdate(mp.getCurrentPosition(), mNativePointer); + } + + + /** + * @param nativePtr is the C++ pointer to the MediaPlayerPrivate object. + */ + public HTML5Audio(int nativePtr) { + // Save the native ptr + mNativePointer = nativePtr; + resetMediaPlayer(); + } + + private void resetMediaPlayer() { + if (mMediaPlayer == null) { + mMediaPlayer = new MediaPlayer(); + } else { + mMediaPlayer.reset(); + } + mMediaPlayer.setOnBufferingUpdateListener(this); + mMediaPlayer.setOnCompletionListener(this); + mMediaPlayer.setOnErrorListener(this); + mMediaPlayer.setOnPreparedListener(this); + mMediaPlayer.setOnSeekCompleteListener(this); + + if (mTimer != null) { + mTimer.cancel(); + } + mTimer = new Timer(); + mState = IDLE; + } + + private void setDataSource(String url) { + mUrl = url; + try { + if (mState != IDLE) { + resetMediaPlayer(); + } + mMediaPlayer.setDataSource(url); + mState = INITIALIZED; + mMediaPlayer.prepareAsync(); + } catch (IOException e) { + Log.e(LOGTAG, "couldn't load the resource: " + url + " exc: " + e); + resetMediaPlayer(); + } + } + + private void play() { + if ((mState == ERROR || mState == IDLE) && mUrl != null) { + resetMediaPlayer(); + setDataSource(mUrl); + mAskToPlay = true; + } + + if (mState >= PREPARED) { + mMediaPlayer.start(); + mState = STARTED; + } + } + + private void pause() { + if (mState == STARTED) { + if (mTimer != null) { + mTimer.purge(); + } + mMediaPlayer.pause(); + mState = PAUSED; + } + } + + private void seek(int msec) { + if (mState >= PREPARED) { + mMediaPlayer.seekTo(msec); + } + } + + private void teardown() { + mMediaPlayer.release(); + mState = ERROR; + mNativePointer = 0; + } + + private float getMaxTimeSeekable() { + return mMediaPlayer.getDuration() / 1000.0f; + } + + private native void nativeOnBuffering(int percent, int nativePointer); + private native void nativeOnEnded(int nativePointer); + private native void nativeOnPrepared(int duration, int width, int height, int nativePointer); + private native void nativeOnTimeupdate(int position, int nativePointer); +} |