WebView and HTML5 <video>

up vote 34 down vote favorite 34

I'm piecing together a cheapo app that amongst other things "frames" some of our websites... Pretty simple with the WebViewClient... until I hit the video.

The video is done as HTML5 elements, and these work fine and dandy on Chrome, iPhones, and now that we fixed the encoding issues it works great on Android... in the native browser.

Now the rub: WebView doesn't like it. At all. I can click on the poster image, and nothing happens.

Googling, I foundhttp://www.codelark.com/2010/05/12/android-viewing-video-from-embedded-webview/which is close, but seems to be based on a 'link' (as in a href...) instead of a video element. (onDownloadListener does not appear to get invoked on video elements...)

I also see references to overriding onShowCustomView, but that seems to not get called on video elements... nor does shouldOverrideUrlLoading..

I would rather not get into "pull xml from the server, reformat it in the app".. by keeping the story layout on the server, I can control the content a bit better without forcing people to keep updating an app. So if I can convince WebView to handle tags like the native browser, that would be best.

I'm clearly missing something obvious.. but I have no clue what.

android html5 video webview android-webview
share | improve this question edited Oct 21 '11 at 7:38 mdelolmo
1,924 6 24
asked Sep 28 '10 at 16:49 brian moore
183 1 3 6
Please see:code.google.com/p/android/issues/detail?id=22254– BoD Nov 25 '11 at 12:28
feedback

12 Answers

active oldest votes
up vote 38 down vote accepted

I answer this topic just in case someone read it and is interested on the result. It is possible to view a video element (video html5 tag) within a WebView, but I must say I had to deal with it for few days. These are the steps I had to follow so far:

-Find a properly encoded video

-When initializing the WebView, set the JavaScript, Plug-ins the WebViewClient and the WebChromeClient.

url = new String("http://broken-links.com/tests/video/"); mWebView = (WebView) findViewById(R.id.webview);mWebView.setWebChromeClient(chromeClient);mWebView.setWebViewClient(wvClient);mWebView.getSettings().setJavaScriptEnabled(true);mWebView.getSettings().setPluginsEnabled(true);mWebView.loadUrl(url);

-Handle the onShowCustomView in the WebChromeClient object.

@Override public void onShowCustomView(View view, CustomViewCallback callback) {   super.onShowCustomView(view, callback);   if (view instanceof FrameLayout){     FrameLayout frame = (FrameLayout) view;     if (frame.getFocusedChild() instanceof VideoView){       VideoView video = (VideoView) frame.getFocusedChild();       frame.removeView(video);       a.setContentView(video);       video.setOnCompletionListener(this);       video.setOnErrorListener(this);       video.start();     }   } } 

-Handle the onCompletion and the onError events for the video, in order to get back to the web view.

public void onCompletion(MediaPlayer mp) {   Log.d(TAG, "Video completo");   a.setContentView(R.layout.main);   WebView wb = (WebView) a.findViewById(R.id.webview);   a.initWebView(); } 

But now I should say there are still an important issue. I can play it only once. The second time I click on the video dispatcher (either the poster or some play button), it does nothing.

I would also like the video to play inside the WebView frame, instead of opening the Media Player window, but this is for me a secondary issue.

I hope it helps somebody, and I would also thank any comment or suggestion.

Saludos, terrícolas.

share | improve this answer answered Oct 7 '10 at 14:34 mdelolmo
1,924 6 24
i donot run any result,my screen is black. i want to know if this need thead. my code is too long to posy here.can you give me some contact way or open a another topic.– pengwang Oct 8 '10 at 2:59
Are you completely sure is not an encoding issue? Try with the URL I posted. Obviously it has to work with the native browser– mdelolmo Oct 8 '10 at 14:49
thanks, mdelolmo! that got my onShowCustomView to work... will have to play with it. Note that the emulator (at least on Linux) doesn't like playing videos much at all, even things a real android will play fine. And encoding is a bit of a pain... took a while to get things set up here so that the droids would stream.– brian moore Oct 8 '10 at 22:44
Thanks mdelolmo! I tried to figure out the issue with it only playing once and I wrote it up in an answer to this question.– littleFluffyKitty Jan 9 '11 at 4:49
9
what is "a"? like that activity would that be?– Collin Price Mar 8 at 3:21
show2more comments feedback
up vote 20 down vote

mdelolmo's answer was incredibly helpful, but like he said, the video only plays once and then you can't open it again.

I looked into this a bit and here is what I found, in case any weary WebView travelers like myself stumble on this post in the future.

First off, I looked at theVideoViewandMediaPlayer's documentation and got a better sense of how those work. I strongly recommend those.

Then, I looked at thesource code to see how the Android Browserdoes it. Do a page find and go look at how they handleonShowCustomView(). They keep a reference to theCustomViewCallbackand to the custom view.

With all of that, and with mdelolmo's answer in mind, when you are done with the video, all you need to do is two things. First, on theVideoViewthat you saved a reference to, callstopPlayback()that will release theMediaPlayerto be used later elsewhere. You can see it in theVideoViewsource code. Second, on theCustomViewCallbackyou saved a reference to callCustomViewCallback.onCustomViewHidden().

After doing those two things, you can click on the same video or any other video and it will open like before. No need to restart the entire WebView.

Hope that helps.

share | improve this answer answered Jan 9 '11 at 4:48 littleFluffyKitty
2,037 1 21 70
Actually I kinda solved it too, I just didn't remember to post the solution (shame on me). Just like you, I had to have a look to Android browser's code, and well, not much to add, I used the onCompletion listener to stop the Player instead and had to set visibility's a couple of times, but I would buy your solution. Regards!– mdelolmo Jan 10 '11 at 7:57
Google Code Search has been retired, I think this version of BrowserActivity.java (from Froyo) roughly corresponds to the one described here:github.com/android/platform_packages_apps_browser/blob/…– spacemanaki Jan 24 at 19:12
@spacemanki's linked code doesn't contain any mentions of VideoView in the whole repo. I guess BrowserActivity does it differently now. I even went back to the Eclair release. Can't find any usage.– Max Howell Mar 22 at 18:56
feedback
up vote 6 down vote

I know this thread is several months old, but I found a solution for playing the video inside the WebView without doing it fullscreen (but still in the media player...). So far, I didn't find any hint on this in the internet so maybe this is also interesting for others. I'm still struggling on some issues (i.e. placing the media player in the right section of the screen, don't know why I'm doing it wrong but it's a relatively small issue I think...).

In the Custom ChromeClient specify LayoutParams:

// 768x512 is the size of my video FrameLayout.LayoutParams LayoutParameters =                   new FrameLayout.LayoutParams (768, 512); 

My onShowCustomView method looks like this:

public void onShowCustomView(final View view, final CustomViewCallback callback) {   // super.onShowCustomView(view, callback);   if (view instanceof FrameLayout) {     this.mCustomViewContainer = (FrameLayout) view;     this.mCustomViewCallback = callback;     this.mContentView = (WebView) this.kameha.findViewById(R.id.webview);     if (this.mCustomViewContainer.getFocusedChild() instanceof VideoView) {       this.mCustomVideoView = (VideoView)                   this.mCustomViewContainer.getFocusedChild();       this.mCustomViewContainer.setVisibility(View.VISIBLE);       final int viewWidth = this.mContentView.getWidth();       final int viewLeft = (viewWidth - 1024) / 2;       // get the x-position for the video (I'm porting an iPad-Webapp to Xoom,        // so I can use those numbers... you have to find your own of course...       this.LayoutParameters.leftMargin = viewLeft + 256;       this.LayoutParameters.topMargin = 128;       // just add this view so the webview underneath will still be visible,        // but apply the LayoutParameters specified above       this.kameha.addContentView(this.mCustomViewContainer,                       this.LayoutParameters);       this.mCustomVideoView.setOnCompletionListener(this);       this.mCustomVideoView.setOnErrorListener(this);       // handle clicks on the screen (turning off the video) so you can still       // navigate in your WebView without having the video lying over it       this.mCustomVideoView.setOnFocusChangeListener(this);       this.mCustomVideoView.start();     }   } } 

So, I hope I could help... I too had to play around with video-Encoding and saw different kinds of using the WebView with html5 video - in the end my working code was a wild mix of different code-parts I found in the internet and some things I had to figure out by myself. It really was a pain in the a*.

share | improve this answer edited Oct 17 '11 at 2:49 Bostone
8,399 8 61 117
answered Aug 9 '11 at 12:09 A-M
61 1 1
feedback
up vote 3 down vote

After long research, I got this thing working. See the following code:

Test.java

import android.app.Activity; import android.os.Bundle; import android.view.KeyEvent; public class Test extends Activity {   HTML5WebView mWebView;   @Override   public void onCreate(Bundle savedInstanceState) {     super.onCreate(savedInstanceState);     mWebView = new HTML5WebView(this);     if (savedInstanceState != null) {       mWebView.restoreState(savedInstanceState);     } else {         mWebView.loadUrl("http://192.168.1.18/xxxxxxxxxxxxxxxx/");     }     setContentView(mWebView.getLayout());   }   @Override   public void onSaveInstanceState(Bundle outState) {     super.onSaveInstanceState(outState);     mWebView.saveState(outState);   }   @Override   public void onStop() {     super.onStop();     mWebView.stopLoading();   }   @Override   public boolean onKeyDown(int keyCode, KeyEvent event) {     if (keyCode == KeyEvent.KEYCODE_BACK) {       if (mWebView.inCustomView()) {         mWebView.hideCustomView();       // mWebView.goBack();         //mWebView.goBack();         return true;       }     }     return super.onKeyDown(keyCode, event);   } } 

HTML%VIDEO.java

package com.ivz.idemandtest; import android.app.Activity; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.util.AttributeSet; import android.util.Log; import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.view.Window; import android.webkit.GeolocationPermissions; import android.webkit.WebChromeClient; import android.webkit.WebSettings; import android.webkit.WebView; import android.webkit.WebViewClient; import android.widget.FrameLayout; public class HTML5WebView extends WebView {   private Context               mContext;   private MyWebChromeClient          mWebChromeClient;   private View                mCustomView;   private FrameLayout             mCustomViewContainer;   private WebChromeClient.CustomViewCallback mCustomViewCallback;   private FrameLayout             mContentView;   private FrameLayout             mBrowserFrameLayout;   private FrameLayout             mLayout;   static final String LOGTAG = "HTML5WebView";   private void init(Context context) {     mContext = context;       Activity a = (Activity) mContext;     mLayout = new FrameLayout(context);     mBrowserFrameLayout = (FrameLayout) LayoutInflater.from(a).inflate(R.layout.custom_screen, null);     mContentView = (FrameLayout) mBrowserFrameLayout.findViewById(R.id.main_content);     mCustomViewContainer = (FrameLayout) mBrowserFrameLayout.findViewById(R.id.fullscreen_custom_content);     mLayout.addView(mBrowserFrameLayout, COVER_SCREEN_PARAMS);     // Configure the webview     WebSettings s = getSettings();     s.setBuiltInZoomControls(true);     s.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.NARROW_COLUMNS);     s.setUseWideViewPort(true);     s.setLoadWithOverviewMode(true);    // s.setSavePassword(true);     s.setSaveFormData(true);     s.setJavaScriptEnabled(true);     mWebChromeClient = new MyWebChromeClient();     setWebChromeClient(mWebChromeClient);     setWebViewClient(new WebViewClient()); setScrollBarStyle(View.SCROLLBARS_INSIDE_OVERLAY);     // enable navigator.geolocation     // s.setGeolocationEnabled(true);    // s.setGeolocationDatabasePath("/data/data/org.itri.html5webview/databases/");     // enable Web Storage: localStorage, sessionStorage     s.setDomStorageEnabled(true);     mContentView.addView(this);   }   public HTML5WebView(Context context) {     super(context);     init(context);   }   public HTML5WebView(Context context, AttributeSet attrs) {     super(context, attrs);     init(context);   }   public HTML5WebView(Context context, AttributeSet attrs, int defStyle) {     super(context, attrs, defStyle);     init(context);   }   public FrameLayout getLayout() {     return mLayout;   }   public boolean inCustomView() {     return (mCustomView != null);   }   public void hideCustomView() {     mWebChromeClient.onHideCustomView();   }   @Override   public boolean onKeyDown(int keyCode, KeyEvent event) {     if (keyCode == KeyEvent.KEYCODE_BACK) {       if ((mCustomView == null) && canGoBack()){         goBack();         return true;       }     }     return super.onKeyDown(keyCode, event);   }   private class MyWebChromeClient extends WebChromeClient {     private Bitmap   mDefaultVideoPoster;     private View    mVideoProgressView;     @Override     public void onShowCustomView(View view, WebChromeClient.CustomViewCallback callback)     {       //Log.i(LOGTAG, "here in on ShowCustomView");       HTML5WebView.this.setVisibility(View.GONE);       // if a view already exists then immediately terminate the new one       if (mCustomView != null) {         callback.onCustomViewHidden();         return;       }       mCustomViewContainer.addView(view);       mCustomView = view;       mCustomViewCallback = callback;       mCustomViewContainer.setVisibility(View.VISIBLE);     }     @Override     public void onHideCustomView() {       System.out.println("customview hideeeeeeeeeeeeeeeeeeeeeeeeeee");       if (mCustomView == null)         return;           // Hide the custom view.       mCustomView.setVisibility(View.GONE);       // Remove the custom view from its container.       mCustomViewContainer.removeView(mCustomView);       mCustomView = null;       mCustomViewContainer.setVisibility(View.GONE);       mCustomViewCallback.onCustomViewHidden();       HTML5WebView.this.setVisibility(View.VISIBLE);       HTML5WebView.this.goBack();       //Log.i(LOGTAG, "set it to webVew");     }     @Override     public View getVideoLoadingProgressView() {       //Log.i(LOGTAG, "here in on getVideoLoadingPregressView");       if (mVideoProgressView == null) {         LayoutInflater inflater = LayoutInflater.from(mContext);         mVideoProgressView = inflater.inflate(R.layout.video_loading_progress, null);       }       return mVideoProgressView;     }     @Override     public void onReceivedTitle(WebView view, String title) {       ((Activity) mContext).setTitle(title);     }     @Override     public void onProgressChanged(WebView view, int newProgress) {       ((Activity) mContext).getWindow().setFeatureInt(Window.FEATURE_PROGRESS, newProgress*100);     }     @Override     public void onGeolocationPermissionsShowPrompt(String origin, GeolocationPermissions.Callback callback) {       callback.invoke(origin, true, false);     }   }   static final FrameLayout.LayoutParams COVER_SCREEN_PARAMS =     new FrameLayout.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT); } 

custom_screen.xml

<?xml version="1.0" encoding="utf-8"?> <!-- Copyright (C) 2009 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. --> <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android">   <FrameLayout android:id="@+id/fullscreen_custom_content"     android:visibility="gone"     android:background="@color/black"     android:layout_width="match_parent"     android:layout_height="match_parent"   />   <LinearLayout android:orientation="vertical"     android:layout_width="match_parent"     android:layout_height="match_parent">     <LinearLayout android:id="@+id/error_console"       android:layout_width="match_parent"       android:layout_height="wrap_content"     />     <FrameLayout android:id="@+id/main_content"       android:layout_width="match_parent"       android:layout_height="match_parent"     />   </LinearLayout> </FrameLayout> 

video_loading_progress.xml

<?xml version="1.0" encoding="utf-8"?> <!-- Copyright (C) 2009 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. --> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"     android:id="@+id/progress_indicator"     android:orientation="vertical"     android:layout_centerInParent="true"     android:layout_width="wrap_content"     android:layout_height="wrap_content">    <ProgressBar android:id="@android:id/progress"      style="?android:attr/progressBarStyleLarge"      android:layout_gravity="center"      android:layout_width="wrap_content"      android:layout_height="wrap_content" />    <TextView android:paddingTop="5dip"      android:layout_width="wrap_content"      android:layout_height="wrap_content"      android:layout_gravity="center"      android:text="@string/loading_video" android:textSize="14sp"      android:textColor="?android:attr/textColorPrimary" /> </LinearLayout> 

colors.xml

<?xml version="1.0" encoding="utf-8"?> <!-- /* //device/apps/common/assets/res/any/http_authentication_colors.xml ** ** Copyright 2006, 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. */ --> <!-- FIXME: Change the name of this file! It is now being used generically   for the browser --> <resources>   <color name="username_text">#ffffffff</color>   <color name="username_edit">#ff000000</color>   <color name="password_text">#ffffffff</color>   <color name="password_edit">#ff000000</color>   <color name="ssl_text_label">#ffffffff</color>   <color name="ssl_text_value">#ffffffff</color>   <color name="white">#ffffffff</color>   <color name="black">#ff000000</color>   <color name="geolocation_permissions_prompt_background">#ffdddddd</color> </resources> 

Manifest.xml

<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android"    package="com.test"    android:versionCode="1"    android:versionName="1.0">   <uses-sdk android:minSdkVersion="7" />   <application android:icon="@drawable/icon" android:label="@string/app_name">     <activity android:name=".Test"          android:label="@string/app_name" android:theme="@android:style/Theme.NoTitleBar.Fullscreen"       android:configChanges="orientation|keyboardHidden|keyboard">       <intent-filter>         <action android:name="android.intent.action.MAIN" />         <category android:name="android.intent.category.LAUNCHER" />       </intent-filter>     </activity>   </application>  <uses-permission android:name="android.permission.INTERNET"></uses-permission> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/> <uses-permission android:name="android.permission.ACCESS_MOCK_LOCATION"/> <uses-permission android:name="android.permission.ACCESS_GPS" /> <uses-permission android:name="android.permission.ACCESS_ASSISTED_GPS" /> <uses-permission android:name="android.permission.ACCESS_LOCATION" /> </manifest> 

Expecting rest of things you can understand.

更多相关文章

  1. 代码中设置drawableleft
  2. android 3.0 隐藏 系统标题栏
  3. Android开发中activity切换动画的实现
  4. Android(安卓)学习 笔记_05. 文件下载
  5. Android中直播视频技术探究之—摄像头Camera视频源数据采集解析
  6. 技术博客汇总
  7. android 2.3 wifi (一)
  8. AndRoid Notification的清空和修改
  9. Android中的Chronometer

随机推荐

  1. 如何让TextView中的文字居中显示
  2. Android(安卓)Jetpack应用指南学习笔记6
  3. [置顶] Android NDK开发轻松入门
  4. Android 消息处理机制(Looper、Handler、M
  5. Android 应用设计--- 无边界设计理念
  6. android应用程序基本原理
  7. android广告赚钱[转]
  8. 《Android Dev Guide》系列教程5:Android
  9. Adobe不再为Android提供Flash,移动Flash死
  10. Android(安卓)阴影问题汇总