android html5 video webview android-webview

android - WebView y HTML5<video>



android-webview (13)

Estoy armando una aplicación cheapo que, entre otras cosas, "enmarca" algunos de nuestros sitios web ... Bastante simple con el WebViewClient . hasta que golpeé el video.

El video se hace como elementos HTML5 , y estos funcionan bien en Chrome, iPhones, y ahora que hemos solucionado los problemas de codificación funciona muy bien en Android en el navegador nativo.

Ahora el problema: WebView no le gusta. En absoluto. Puedo hacer clic en la imagen del póster y no pasa nada.

Google, encontré this que está cerca, pero parece estar basado en un ''enlace'' (como en un href ...) en lugar de un elemento de video. (onDownloadListener no parece ser invocado en elementos de video ...)

También veo referencias a reemplazar OverShowCustomView, pero parece que no se llama en los elementos de video ... ni tampoco shouldoverrideUrlLoading.

Preferiría no entrar en "extraer xml del servidor, formatearlo en la aplicación" ... manteniendo el diseño de la historia en el servidor, puedo controlar el contenido un poco mejor sin forzar a las personas a seguir actualizando una aplicación. Entonces, si puedo convencer a WebView para manejar etiquetas como el navegador nativo, sería lo mejor.

Claramente me falta algo obvio ... pero no tengo idea de qué.


AM es similar a lo que hace BrowerActivity . para FrameLayout.LayoutParams LayoutParameters = new FrameLayout.LayoutParams (768, 512);

Creo que podemos usar

FrameLayout.LayoutParams LayoutParameters = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.FILL_PARENT, FrameLayout.LayoutParams.FILL_PARENT)

en lugar.

Otro problema que encontré es si el video se está reproduciendo, y el usuario hace clic en el botón Atrás, la próxima vez va a esta actividad (solo a la cima) y no puede reproducir el video. para arreglar esto, llamé al

try { mCustomVideoView.stopPlayback(); mCustomViewCallback.onCustomViewHidden(); } catch(Throwable e) { //ignore }

en el método onBackPressed de la actividad.


Bueno, aparentemente esto no es posible sin usar un JNI para registrar un complemento para obtener el evento de video. (Personalmente, estoy evitando JNI ya que realmente no quiero lidiar con un desastre cuando las tabletas de Android basadas en Atom salen en los próximos meses, perdiendo la portabilidad de Java).

La única alternativa real parece ser crear una nueva página web solo para WebView y hacer un video de la vieja escuela con un enlace A HREF como se cita en la url Codelark anterior.

Icky.


Después de una larga investigación, hice que esto funcionara. Vea el siguiente código:

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>

Esperando el resto de cosas que puedas entender.


En honeycomb use hardwareaccelerated=true y pluginstate.on_demand parece funcionar


En realidad, parece suficiente simplemente adjuntar un stock WebChromeClient a la vista del cliente, ala

mWebView.setWebChromeClient(new WebChromeClient());

¡y necesitas tener aceleración de hardware activada!

Al menos, si no necesita reproducir un video de pantalla completa, no hay necesidad de sacar el VideoView del WebView y colocarlo en la vista de la actividad. Jugará en el rect adjudicado del elemento de video.

¿Alguna idea de cómo interceptar el botón expandir video?


Esta pregunta tiene años, pero tal vez mi respuesta ayude a las personas como yo que tienen que soportar la versión anterior de Android. Probé muchos enfoques diferentes que funcionaban en algunas versiones de Android, pero no en todas. La mejor solución que encontré es usar Crosswalk Webview, que está optimizado para el soporte de funciones HTML5 y funciona en Android 4.1 y versiones posteriores. Es tan simple de usar como el Android WebView predeterminado. Solo tienes que incluir la biblioteca. Aquí puede encontrar un tutorial simple sobre cómo usarlo: https://diego.org/2015/01/07/embedding-crosswalk-in-android-studio/


Este enfoque funciona bien hasta 2.3 Y al agregar hardwareaccelerated = true incluso funciona de 3.0 a ICS. Un problema al que me enfrento actualmente es cuando el segundo lanzamiento de la aplicación de reproductor de medios se cuelga porque no he detenido la reproducción y he liberado el reproductor multimedia. Como objeto VideoSurfaceView, que obtenemos en la función ShowView Personalizado del sistema operativo 3.0, son específicos del navegador y no un objeto VideoView como en, hasta el sistema operativo 2.3 ¿Cómo puedo acceder a él, detener Playback y liberar recursos?


La respuesta de mdelolmo fue increíblemente útil, pero como dijo, el video solo se reproduce una vez y luego no se puede volver a abrir.

Analicé esto un poco y esto es lo que encontré, en caso de que cualquier viajero cansado de WebView como yo tropiece con esta publicación en el futuro.

En primer lugar, miré la documentación de VideoView y MediaPlayer y obtuve una mejor idea de cómo funcionan. Recomiendo esos.

Luego, miré el código fuente para ver cómo lo hace el navegador Android . Haga una búsqueda de página e ir a ver cómo se manejan en onShowCustomView() . Mantienen una referencia a CustomViewCallback y a la vista personalizada.

Con todo eso, y con la respuesta de mdelolmo en mente, cuando haya terminado con el video, todo lo que necesita hacer es dos cosas. Primero, en el VideoView que guardó una referencia, llame a stopPlayback() que liberará el MediaPlayer para usarlo luego en otro lugar. Puedes verlo en el código fuente de VideoView . En segundo lugar, en CustomViewCallback guardó una referencia para llamar a CustomViewCallback.onCustomViewHidden() .

Después de hacer esas dos cosas, puede hacer clic en el mismo video o en cualquier otro video y se abrirá como antes. No es necesario reiniciar toda WebView.

Espero que ayude.


Respondo este tema solo en caso de que alguien lo lea y esté interesado en el resultado. Es posible ver un elemento de video (etiqueta de video html5) dentro de un WebView, pero debo decir que tuve que lidiar con él por unos días. Estos son los pasos que tuve que seguir hasta ahora:

-Busca un video codificado correctamente

- Al inicializar WebView, configure el JavaScript, los complementos, el WebViewClient y el 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().setPluginState(PluginState.ON); mWebView.loadUrl(url);

-Manipule onShowCustomView en el objeto WebChromeClient.

@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(); } } }

-Manipule los eventos onCompletion y onError para el video, para volver a la vista web.

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(); }

Pero ahora debo decir que todavía hay un problema importante. Puedo jugarlo solo una vez La segunda vez que hago clic en el despachador de video (ya sea el póster o algún botón de reproducción), no hace nada.

También me gustaría que el video se reproduzca dentro del marco de WebView, en lugar de abrir la ventana de Media Player, pero esto es para mí un problema secundario.

Espero que ayude a alguien, y también agradecería cualquier comentario o sugerencia.

Saludos, terrícolas.


Sé que esta es una pregunta muy antigua, pero ¿has probado el indicador de manifiesto hardwareAccelerated="true" para tu aplicación o actividad?

Con este conjunto, parece funcionar sin ninguna modificación de WebChromeClient (lo que esperaría de un elemento DOM).


Sé que este hilo tiene varios meses, pero encontré una solución para reproducir el video dentro de WebView sin hacerlo a pantalla completa (pero aún en el reproductor multimedia ...). Hasta ahora, no encontré ninguna pista sobre esto en Internet, así que tal vez esto también sea interesante para otros. Todavía estoy luchando en algunos asuntos (es decir, colocar el reproductor multimedia en la sección derecha de la pantalla, no sé por qué lo estoy haciendo mal, pero creo que es un problema relativamente pequeño ...).

En Custom ChromeClient, especifique LayoutParams:

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

Mi método onShowCustomView se ve así:

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(); } } }

Así que, espero poder ayudar ... También tuve que jugar con Video-Encoding y vi diferentes tipos de uso de WebView con video html5 - al final, mi código de trabajo era una mezcla salvaje de diferentes partes de código que encontré en Internet y algunas cosas que tuve que resolver solo. Realmente fue un dolor en el a *.


Tuve un problema similar. Tenía archivos y videos HTML en la carpeta assets de mi aplicación.

Por lo tanto, los videos se ubicaron dentro del APK. Como el APK es realmente un archivo ZIP, WebView no pudo leer los archivos de video.

Copiar todos los archivos HTML y de video en la tarjeta SD funcionó para mí.


code.google.com/p/html5webview para resolver este problema. code.google.com/p/html5webview y póngalo en su proyecto, luego puede codificar así.

private HTML5WebView mWebView; String url = "SOMEURL"; public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mWebView = new HTML5WebView(this); if (savedInstanceState != null) { mWebView.restoreState(savedInstanceState); } else { mWebView.loadUrl(url); } setContentView(mWebView.getLayout()); } @Override public void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); mWebView.saveState(outState); }

Para hacer que el video sea rotativo, coloque el código android: configChanges = "orientation" en su Activity por ejemplo (Androidmanifest.xml)

<activity android:name=".ui.HTML5Activity" android:configChanges="orientation"/>

y anula el método onConfigurationChanged.

@Override public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); }