WebView and HTML5 <video>

后端 未结 13 1663
面向向阳花
面向向阳花 2020-11-22 05:59

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.

相关标签:
13条回答
  • 2020-11-22 06:17

    This question is years old, but maybe my answer will help people like me who have to support old Android version. I tried a lot of different approaches which worked on some Android versions, however not on all. The best solution I found is to use the Crosswalk Webview which is optimized for HTML5 feature support and works on Android 4.1 and higher. It is as simple to use as the default Android WebView. You just have to include the library. Here you can find a simple tutorial on how to use it: https://diego.org/2015/01/07/embedding-crosswalk-in-android-studio/

    0 讨论(0)
  • 2020-11-22 06:20

    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*.

    0 讨论(0)
  • 2020-11-22 06:24

    On honeycomb use hardwareaccelerated=true and pluginstate.on_demand seems to work

    0 讨论(0)
  • 2020-11-22 06:25

    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">#ffffffdffffd</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.

    0 讨论(0)
  • 2020-11-22 06:25

    I had a similar problem. I had HTML files and videos in the assets-folder of my app.

    Therefore the videos were located inside of the APK. Because the APK is really a ZIP-file, the WebView was not able to read the video-files.

    Copying all HTML- and video-files onto the SD-Card worked for me.

    0 讨论(0)
  • 2020-11-22 06:28

    A-M's is similar to what the BrowerActivity does. for FrameLayout.LayoutParams LayoutParameters = new FrameLayout.LayoutParams (768, 512);

    I think we can use

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

    instead.

    Another issue I met is if the video is playing, and user clicks the back button, next time, you go to this activity(singleTop one) and can not play the video. to fix this, I called the

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

    in the activity's onBackPressed method.

    0 讨论(0)
提交回复
热议问题