Memutar video HTML5 pada layar penuh dalam tampilan web android

92

Nah, saya sudah mencari beberapa hari, bagaimana menampilkan video HTML5 dalam mode layar penuh di WebView android.

Saya berhasil memutar video HTML5 di tampilan web saya. Masalah muncul saat menampilkan video dalam mode layar penuh.

Seperti yang saya ketahui, android memiliki dua cara menangani tag <video>:

  1. Pada versi android <= 2.3.3 , metode onShowCustomView diaktifkan, dan saya bisa memiliki instance VideoView, dan menyetel listener saat video selesai, menyetel pengontrol, dll. Sejauh ini bagus.

  2. Di ICS (dan mungkin 3.0 ke atas) , tampaknya <video> ditangani dengan cara yang berbeda. Saat video HTML5 diputar, onShowCustomView tidak dipanggil dalam mode normal - sepertinya ada bisnis internal di dalam WebView yang memutar video, dan semua kontrol yang ditentukan dalam tag <video> ditampilkan - I tidak dapat mengaksesnya dengan cara apa pun. Sebenarnya, jika video diputar dalam mode normal ini tidak masalah karena kontrolnya ada dan berfungsi.

Hal itu membawa saya ke masalah besar: saat menampilkan video dalam mode layar penuh, onShowCustomView dipanggil - tetapi di ICS, parameter "view" bukan merupakan instance dari VideoView.

Saya berhasil menemukan bahwa instance tersebut adalah VideoSurfaceView, kelas dalam pribadi dari kelas HTML5VideoFullScreen. Satu-satunya cara kita dapat mengakses kelas dalam ini adalah melalui refleksi.

Setelah melihat GrepCode untuk kelas ini, saya mengetahui bahwa tidak seperti VideoView, HTML5VideoFullScreen $ VideoSurfaceView tidak menyimpan instance MediaPlayer sehingga saya dapat mendengarkan kejadiannya atau mengakses kontrolnya. Satu-satunya hal yang dapat saya lakukan adalah mengambil VideoSurfaceView ini apa adanya dan meletakkannya di dalam tata letak layar penuh tanpa mengontrolnya.

Intinya - Saat menampilkan video dalam layar penuh, saya tidak tahu kapan video berakhir, kontrolnya tidak ditampilkan - ini sangat menyedihkan. Saya tidak bisa mendapatkan pemicu untuk menutup layar penuh.

Saya mencoba beberapa solusi yang tidak berhasil:

  1. Refleksi: Saya mencoba menjangkau instance HTML5VideoFullScreen, yang menampung anggota MediaPlayer, dari VideoSurfaceView kelas dalam. Saya tidak berhasil mendapatkannya, saya tidak yakin ini mungkin (ViewSurfaceView tidak menyimpan instance pemiliknya).

  2. Mendaftar untuk acara video melalui Javascript (onended, misalnya), dan menangani apa yang saya butuhkan kembali di JAVA melalui Javascript Antarmuka: Saya menemukan solusi ini tidak dapat diandalkan karena saat melakukan ini saya mengalami masalah lain: tag <video> dapat bersarang di sebuah. Sumber iframe bukan milik saya dan saya tidak bisa mendapatkan kontennya (getElementById atau getElementsByTagName [i] adalah nulls) - yang berarti, saya tidak dapat menjangkau elemen <video> di dalam iframe.

Saya masih mencari solusi, sangat sedikit yang ditulis tentang masalah ini. Apakah ada yang berhasil menyelesaikannya? Bantuan akan sangat dihargai!

Kelas VideoView : Di sini (memiliki MediaPlayer)

HTML5VideoFullScreen $ VideoSurfaceView class: Di sini (tidak memiliki MediaPlayer)

nbtk
sumber
Masalah yang sama seperti Anda, dan mencapai kesimpulan yang sama juga. Saya telah mengerjakan ini selama sekitar 20 jam, tetapi tidak akan menyerah setidaknya selama 20 jam lebih (jam kerja nyata, maksud saya). Akan memberi tahu Anda jika saya menemukan solusinya. Sementara itu, karena pertanyaan ini diajukan 20 hari yang lalu, apakah Anda masih mencoba untuk menyelesaikannya atau apakah Anda menerapkan solusi lain?
cprcrack
Ngomong-ngomong, sekilas melihat kode sumber HTML5VideoFullScreen menunjukkan bahwa kelas induknya HTML5VideoView berisi MediaPlayer.
cprcrack

Jawaban:

176

Sunting 2014/10: dengan permintaan populer Saya mempertahankan dan memindahkan ini ke GitHub. Silakan periksa cprcrack / VideoEnabledWebView untuk versi terakhir. Akan menyimpan jawaban ini hanya untuk referensi.

Edit 2014/01: peningkatan penggunaan contoh untuk menyertakan tampilan nonVideoLayout, videoLayout, dan videoLoading, bagi pengguna yang meminta lebih banyak kode contoh untuk pemahaman yang lebih baik.

Sunting 2013/12: beberapa perbaikan bug yang terkait dengan kompatibilitas perangkat Sony Xperia, tetapi pada kenyataannya memengaruhi semua perangkat.

Sunting 2013/11: setelah rilis Android 4.4 KitKat (API level 19) dengan tampilan web Chromium barunya, saya harus bekerja keras lagi. Beberapa perbaikan dilakukan. Anda harus memperbarui ke versi baru ini. Saya merilis sumber ini di bawah WTFPL .

Sunting 2013/04: setelah 1 minggu kerja keras, saya akhirnya mencapai semua yang saya butuhkan. Saya pikir dua kelas generik yang saya buat ini dapat menyelesaikan semua masalah Anda.

VideoEnabledWebChromeClientdapat digunakan sendiri jika Anda tidak memerlukan fungsionalitas yang VideoEnabledWebViewmenambahkan. Namun VideoEnabledWebViewharus selalu mengandalkan a VideoEnabledWebChromeClient. Harap baca semua komentar dari kedua kelas dengan seksama.

VideoEnabledWebChromeClient

import android.media.MediaPlayer;
import android.media.MediaPlayer.OnCompletionListener;
import android.media.MediaPlayer.OnErrorListener;
import android.media.MediaPlayer.OnPreparedListener;
import android.view.SurfaceView;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
import android.webkit.WebChromeClient;
import android.widget.FrameLayout;

/**
 * This class serves as a WebChromeClient to be set to a WebView, allowing it to play video.
 * Video will play differently depending on target API level (in-line, fullscreen, or both).
 *
 * It has been tested with the following video classes:
 * - android.widget.VideoView (typically API level <11)
 * - android.webkit.HTML5VideoFullScreen$VideoSurfaceView/VideoTextureView (typically API level 11-18)
 * - com.android.org.chromium.content.browser.ContentVideoView$VideoSurfaceView (typically API level 19+)
 * 
 * Important notes:
 * - For API level 11+, android:hardwareAccelerated="true" must be set in the application manifest.
 * - The invoking activity must call VideoEnabledWebChromeClient's onBackPressed() inside of its own onBackPressed().
 * - Tested in Android API levels 8-19. Only tested on http://m.youtube.com.
 *
 * @author Cristian Perez (http://cpr.name)
 *
 */
public class VideoEnabledWebChromeClient extends WebChromeClient implements OnPreparedListener, OnCompletionListener, OnErrorListener
{
    public interface ToggledFullscreenCallback
    {
        public void toggledFullscreen(boolean fullscreen);
    }

    private View activityNonVideoView;
    private ViewGroup activityVideoView;
    private View loadingView;
    private VideoEnabledWebView webView;

    private boolean isVideoFullscreen; // Indicates if the video is being displayed using a custom view (typically full-screen)
    private FrameLayout videoViewContainer;
    private CustomViewCallback videoViewCallback;

    private ToggledFullscreenCallback toggledFullscreenCallback;

    /**
     * Never use this constructor alone.
     * This constructor allows this class to be defined as an inline inner class in which the user can override methods
     */
    @SuppressWarnings("unused")
    public VideoEnabledWebChromeClient()
    {
    }

    /**
     * Builds a video enabled WebChromeClient.
     * @param activityNonVideoView A View in the activity's layout that contains every other view that should be hidden when the video goes full-screen.
     * @param activityVideoView A ViewGroup in the activity's layout that will display the video. Typically you would like this to fill the whole layout.
     */
    @SuppressWarnings("unused")
    public VideoEnabledWebChromeClient(View activityNonVideoView, ViewGroup activityVideoView)
    {
        this.activityNonVideoView = activityNonVideoView;
        this.activityVideoView = activityVideoView;
        this.loadingView = null;
        this.webView = null;
        this.isVideoFullscreen = false;
    }

    /**
     * Builds a video enabled WebChromeClient.
     * @param activityNonVideoView A View in the activity's layout that contains every other view that should be hidden when the video goes full-screen.
     * @param activityVideoView A ViewGroup in the activity's layout that will display the video. Typically you would like this to fill the whole layout.
     * @param loadingView A View to be shown while the video is loading (typically only used in API level <11). Must be already inflated and without a parent view.
     */
    @SuppressWarnings("unused")
    public VideoEnabledWebChromeClient(View activityNonVideoView, ViewGroup activityVideoView, View loadingView)
    {
        this.activityNonVideoView = activityNonVideoView;
        this.activityVideoView = activityVideoView;
        this.loadingView = loadingView;
        this.webView = null;
        this.isVideoFullscreen = false;
    }

    /**
     * Builds a video enabled WebChromeClient.
     * @param activityNonVideoView A View in the activity's layout that contains every other view that should be hidden when the video goes full-screen.
     * @param activityVideoView A ViewGroup in the activity's layout that will display the video. Typically you would like this to fill the whole layout.
     * @param loadingView A View to be shown while the video is loading (typically only used in API level <11). Must be already inflated and without a parent view.
     * @param webView The owner VideoEnabledWebView. Passing it will enable the VideoEnabledWebChromeClient to detect the HTML5 video ended event and exit full-screen.
     * Note: The web page must only contain one video tag in order for the HTML5 video ended event to work. This could be improved if needed (see Javascript code).
     */
    public VideoEnabledWebChromeClient(View activityNonVideoView, ViewGroup activityVideoView, View loadingView, VideoEnabledWebView webView)
    {
        this.activityNonVideoView = activityNonVideoView;
        this.activityVideoView = activityVideoView;
        this.loadingView = loadingView;
        this.webView = webView;
        this.isVideoFullscreen = false;
    }

    /**
     * Indicates if the video is being displayed using a custom view (typically full-screen)
     * @return true it the video is being displayed using a custom view (typically full-screen)
     */
    public boolean isVideoFullscreen()
    {
        return isVideoFullscreen;
    }

    /**
     * Set a callback that will be fired when the video starts or finishes displaying using a custom view (typically full-screen)
     * @param callback A VideoEnabledWebChromeClient.ToggledFullscreenCallback callback
     */
    public void setOnToggledFullscreen(ToggledFullscreenCallback callback)
    {
        this.toggledFullscreenCallback = callback;
    }

    @Override
    public void onShowCustomView(View view, CustomViewCallback callback)
    {
        if (view instanceof FrameLayout)
        {
            // A video wants to be shown
            FrameLayout frameLayout = (FrameLayout) view;
            View focusedChild = frameLayout.getFocusedChild();

            // Save video related variables
            this.isVideoFullscreen = true;
            this.videoViewContainer = frameLayout;
            this.videoViewCallback = callback;

            // Hide the non-video view, add the video view, and show it
            activityNonVideoView.setVisibility(View.INVISIBLE);
            activityVideoView.addView(videoViewContainer, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
            activityVideoView.setVisibility(View.VISIBLE);

            if (focusedChild instanceof android.widget.VideoView)
            {
                // android.widget.VideoView (typically API level <11)
                android.widget.VideoView videoView = (android.widget.VideoView) focusedChild;

                // Handle all the required events
                videoView.setOnPreparedListener(this);
                videoView.setOnCompletionListener(this);
                videoView.setOnErrorListener(this);
            }
            else
            {
                // Other classes, including:
                // - android.webkit.HTML5VideoFullScreen$VideoSurfaceView, which inherits from android.view.SurfaceView (typically API level 11-18)
                // - android.webkit.HTML5VideoFullScreen$VideoTextureView, which inherits from android.view.TextureView (typically API level 11-18)
                // - com.android.org.chromium.content.browser.ContentVideoView$VideoSurfaceView, which inherits from android.view.SurfaceView (typically API level 19+)

                // Handle HTML5 video ended event only if the class is a SurfaceView
                // Test case: TextureView of Sony Xperia T API level 16 doesn't work fullscreen when loading the javascript below
                if (webView != null && webView.getSettings().getJavaScriptEnabled() && focusedChild instanceof SurfaceView)
                {
                    // Run javascript code that detects the video end and notifies the Javascript interface
                    String js = "javascript:";
                    js += "var _ytrp_html5_video_last;";
                    js += "var _ytrp_html5_video = document.getElementsByTagName('video')[0];";
                    js += "if (_ytrp_html5_video != undefined && _ytrp_html5_video != _ytrp_html5_video_last) {";
                    {
                        js += "_ytrp_html5_video_last = _ytrp_html5_video;";
                        js += "function _ytrp_html5_video_ended() {";
                        {
                            js += "_VideoEnabledWebView.notifyVideoEnd();"; // Must match Javascript interface name and method of VideoEnableWebView
                        }
                        js += "}";
                        js += "_ytrp_html5_video.addEventListener('ended', _ytrp_html5_video_ended);";
                    }
                    js += "}";
                    webView.loadUrl(js);
                }
            }

            // Notify full-screen change
            if (toggledFullscreenCallback != null)
            {
                toggledFullscreenCallback.toggledFullscreen(true);
            }
        }
    }

    @Override @SuppressWarnings("deprecation")
    public void onShowCustomView(View view, int requestedOrientation, CustomViewCallback callback) // Available in API level 14+, deprecated in API level 18+
    {
        onShowCustomView(view, callback);
    }

    @Override
    public void onHideCustomView()
    {
        // This method should be manually called on video end in all cases because it's not always called automatically.
        // This method must be manually called on back key press (from this class' onBackPressed() method).

        if (isVideoFullscreen)
        {
            // Hide the video view, remove it, and show the non-video view
            activityVideoView.setVisibility(View.INVISIBLE);
            activityVideoView.removeView(videoViewContainer);
            activityNonVideoView.setVisibility(View.VISIBLE);

            // Call back (only in API level <19, because in API level 19+ with chromium webview it crashes)
            if (videoViewCallback != null && !videoViewCallback.getClass().getName().contains(".chromium."))
            {
                videoViewCallback.onCustomViewHidden();
            }

            // Reset video related variables
            isVideoFullscreen = false;
            videoViewContainer = null;
            videoViewCallback = null;

            // Notify full-screen change
            if (toggledFullscreenCallback != null)
            {
                toggledFullscreenCallback.toggledFullscreen(false);
            }
        }
    }

    @Override
    public View getVideoLoadingProgressView() // Video will start loading
    {
        if (loadingView != null)
        {
            loadingView.setVisibility(View.VISIBLE);
            return loadingView;
        }
        else
        {
            return super.getVideoLoadingProgressView();
        }
    }

    @Override
    public void onPrepared(MediaPlayer mp) // Video will start playing, only called in the case of android.widget.VideoView (typically API level <11)
    {
        if (loadingView != null)
        {
            loadingView.setVisibility(View.GONE);
        }
    }

    @Override
    public void onCompletion(MediaPlayer mp) // Video finished playing, only called in the case of android.widget.VideoView (typically API level <11)
    {
        onHideCustomView();
    }

    @Override
    public boolean onError(MediaPlayer mp, int what, int extra) // Error while playing video, only called in the case of android.widget.VideoView (typically API level <11)
    {
        return false; // By returning false, onCompletion() will be called
    }

    /**
     * Notifies the class that the back key has been pressed by the user.
     * This must be called from the Activity's onBackPressed(), and if it returns false, the activity itself should handle it. Otherwise don't do anything.
     * @return Returns true if the event was handled, and false if was not (video view is not visible)
     */
    public boolean onBackPressed()
    {
        if (isVideoFullscreen)
        {
            onHideCustomView();
            return true;
        }
        else
        {
            return false;
        }
    }

}

VideoEnabledWebView

import android.annotation.SuppressLint;
import android.content.Context;
import android.os.Handler;
import android.os.Looper;
import android.util.AttributeSet;
import android.webkit.WebChromeClient;
import android.webkit.WebView;

import java.util.Map;

/**
 * This class serves as a WebView to be used in conjunction with a VideoEnabledWebChromeClient.
 * It makes possible:
 * - To detect the HTML5 video ended event so that the VideoEnabledWebChromeClient can exit full-screen.
 * 
 * Important notes:
 * - Javascript is enabled by default and must not be disabled with getSettings().setJavaScriptEnabled(false).
 * - setWebChromeClient() must be called before any loadData(), loadDataWithBaseURL() or loadUrl() method.
 *
 * @author Cristian Perez (http://cpr.name)
 *
 */
public class VideoEnabledWebView extends WebView
{
    public class JavascriptInterface
    {
        @android.webkit.JavascriptInterface
        public void notifyVideoEnd() // Must match Javascript interface method of VideoEnabledWebChromeClient
        {
            // This code is not executed in the UI thread, so we must force that to happen
            new Handler(Looper.getMainLooper()).post(new Runnable()
            {
                @Override
                public void run()
                {
                    if (videoEnabledWebChromeClient != null)
                    {
                        videoEnabledWebChromeClient.onHideCustomView();
                    }
                }
            });
        }
    }

    private VideoEnabledWebChromeClient videoEnabledWebChromeClient;
    private boolean addedJavascriptInterface;

    public VideoEnabledWebView(Context context)
    {
        super(context);
        addedJavascriptInterface = false;
    }

    @SuppressWarnings("unused")
    public VideoEnabledWebView(Context context, AttributeSet attrs)
    {
        super(context, attrs);
        addedJavascriptInterface = false;
    }

    @SuppressWarnings("unused")
    public VideoEnabledWebView(Context context, AttributeSet attrs, int defStyle)
    {
        super(context, attrs, defStyle);
        addedJavascriptInterface = false;
    }

    /**
     * Indicates if the video is being displayed using a custom view (typically full-screen)
     * @return true it the video is being displayed using a custom view (typically full-screen)
     */
    public boolean isVideoFullscreen()
    {
        return videoEnabledWebChromeClient != null && videoEnabledWebChromeClient.isVideoFullscreen();
    }

    /**
     * Pass only a VideoEnabledWebChromeClient instance.
     */
    @Override @SuppressLint("SetJavaScriptEnabled")
    public void setWebChromeClient(WebChromeClient client)
    {
        getSettings().setJavaScriptEnabled(true);

        if (client instanceof VideoEnabledWebChromeClient)
        {
            this.videoEnabledWebChromeClient = (VideoEnabledWebChromeClient) client;
        }

        super.setWebChromeClient(client);
    }

    @Override
    public void loadData(String data, String mimeType, String encoding)
    {
        addJavascriptInterface();
        super.loadData(data, mimeType, encoding);
    }

    @Override
    public void loadDataWithBaseURL(String baseUrl, String data, String mimeType, String encoding, String historyUrl)
    {
        addJavascriptInterface();
        super.loadDataWithBaseURL(baseUrl, data, mimeType, encoding, historyUrl);
    }

    @Override
    public void loadUrl(String url)
    {
        addJavascriptInterface();
        super.loadUrl(url);
    }

    @Override
    public void loadUrl(String url, Map<String, String> additionalHttpHeaders)
    {
        addJavascriptInterface();
        super.loadUrl(url, additionalHttpHeaders);
    }

    private void addJavascriptInterface()
    {
        if (!addedJavascriptInterface)
        {
            // Add javascript interface to be called when the video ends (must be done before page load)
            addJavascriptInterface(new JavascriptInterface(), "_VideoEnabledWebView"); // Must match Javascript interface name of VideoEnabledWebChromeClient

            addedJavascriptInterface = true;
        }
    }

}

Contoh penggunaan:

Tata letak utama activity_main.xml tempat kita meletakkan VideoEnabledWebView dan tampilan lain yang digunakan:

<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" >

    <!-- View that will be hidden when video goes fullscreen -->
    <RelativeLayout
        android:id="@+id/nonVideoLayout"
        android:layout_width="match_parent"
        android:layout_height="match_parent" >

        <your.package.VideoEnabledWebView
            android:id="@+id/webView"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />

    </RelativeLayout>   

    <!-- View where the video will be shown when video goes fullscreen -->
    <RelativeLayout
        android:id="@+id/videoLayout"
        android:layout_width="match_parent"
        android:layout_height="match_parent" >

        <!-- View that will be shown while the fullscreen video loads (maybe include a spinner and a "Loading..." message) -->
        <View
            android:id="@+id/videoLoading"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerInParent="true"
            android:visibility="invisible" />

    </RelativeLayout>

</RelativeLayout>

Aktivitas onCreate () , di mana kita menginisialisasinya:

private VideoEnabledWebView webView;
private VideoEnabledWebChromeClient webChromeClient;

@Override
protected void onCreate(Bundle savedInstanceState)
{
    super.onCreate(savedInstanceState);

    // Set layout
    setContentView(R.layout.activity_main);

    // Save the web view
    webView = (VideoEnabledWebView) findViewById(R.id.webView);

    // Initialize the VideoEnabledWebChromeClient and set event handlers
    View nonVideoLayout = findViewById(R.id.nonVideoLayout); // Your own view, read class comments
    ViewGroup videoLayout = (ViewGroup) findViewById(R.id.videoLayout); // Your own view, read class comments
    View loadingView = getLayoutInflater().inflate(R.layout.view_loading_video, null); // Your own view, read class comments
    webChromeClient = new VideoEnabledWebChromeClient(nonVideoLayout, videoLayout, loadingView, webView) // See all available constructors...
    {
        // Subscribe to standard events, such as onProgressChanged()...
        @Override
        public void onProgressChanged(WebView view, int progress)
        {
            // Your code...
        }
    };
    webChromeClient.setOnToggledFullscreen(new VideoEnabledWebChromeClient.ToggledFullscreenCallback()
    {
        @Override
        public void toggledFullscreen(boolean fullscreen)
        {
            // Your code to handle the full-screen change, for example showing and hiding the title bar. Example:
            if (fullscreen)
            {
                WindowManager.LayoutParams attrs = getWindow().getAttributes();
                attrs.flags |= WindowManager.LayoutParams.FLAG_FULLSCREEN;
                attrs.flags |= WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON;
                getWindow().setAttributes(attrs);
                if (android.os.Build.VERSION.SDK_INT >= 14)
                {
                    getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LOW_PROFILE);
                }
            }
            else
            {
                WindowManager.LayoutParams attrs = getWindow().getAttributes();
                attrs.flags &= ~WindowManager.LayoutParams.FLAG_FULLSCREEN;
                attrs.flags &= ~WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON;
                getWindow().setAttributes(attrs);
                if (android.os.Build.VERSION.SDK_INT >= 14)
                {
                    getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_VISIBLE);
                }
            }

        }
    });
    webView.setWebChromeClient(webChromeClient);

    // Navigate everywhere you want, this classes have only been tested on YouTube's mobile site
    webView.loadUrl("http://m.youtube.com");
}

Dan jangan lupa untuk memanggil onBackPressed () :

@Override
public void onBackPressed()
{
    // Notify the VideoEnabledWebChromeClient, and handle it ourselves if it doesn't handle it
    if (!webChromeClient.onBackPressed())
    {
        if (webView.canGoBack())
        {
            webView.goBack();
        }
        else
        {
            // Close app (presumably)
            super.onBackPressed();
        }
    }
}
cprcrack
sumber
1
Apa yang Anda sarankan, akan diberitahukan saat video berakhir hanya di API <= 10. On> = 11, tidak ada yang mendaftarkan onCompletionListener ...
nbtk
4
Terima kasih atas usaha Anda! Tetapi jika <video> berada di bawah <iframe>, kita tidak dapat menjangkau itu, jadi bagian javacsript tidak sempurna. Kode Anda sangat profesional, mengesankan.
nbtk
3
Memasukkan kode jawaban stackoverflow.com/questions/20379478/… ini akan menyelesaikan masalah yang saya ajukan di atas.
Wienke Giezeman
2
Perlu upgrade, tidak berfungsi di android v4.4.4 +, tambahkan opsi untuk bermain, otomatis masuk ke layar penuh. Anda dapat menerapkan vimeo pada kode ini? Terima kasih telah membagikan kode di github. @cprcrack
Florida
2
itu tidak berfungsi di versi adroid terbaru ... di atas 5.0, 6.0,7.0
UMAR-MOBITSOLUTIONS
12

Diuji pada versi Android 9.0

Tidak ada jawaban yang berhasil untuk saya. Ini adalah hal terakhir yang berhasil

import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.webkit.WebChromeClient;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.FrameLayout;
import android.widget.ProgressBar;

public class MainActivity extends AppCompatActivity {

    WebView mWebView;


    @SuppressLint("SetJavaScriptEnabled")
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);


        mWebView = (WebView) findViewById(R.id.webView);


        mWebView.setWebViewClient(new WebViewClient());
        mWebView.setWebChromeClient(new MyChrome());
        WebSettings webSettings = mWebView.getSettings();
        webSettings.setJavaScriptEnabled(true);
        webSettings.setAllowFileAccess(true);
        webSettings.setAppCacheEnabled(true);

       if (savedInstanceState == null) {
          mWebView.loadUrl("https://www.youtube.com/");
       }

    }


    private class MyChrome extends WebChromeClient {

        private View mCustomView;
        private WebChromeClient.CustomViewCallback mCustomViewCallback;
        protected FrameLayout mFullscreenContainer;
        private int mOriginalOrientation;
        private int mOriginalSystemUiVisibility;

        MyChrome() {}

        public Bitmap getDefaultVideoPoster()
        {
            if (mCustomView == null) {
                return null;
            }
            return BitmapFactory.decodeResource(getApplicationContext().getResources(), 2130837573);
        }

        public void onHideCustomView()
        {
            ((FrameLayout)getWindow().getDecorView()).removeView(this.mCustomView);
            this.mCustomView = null;
            getWindow().getDecorView().setSystemUiVisibility(this.mOriginalSystemUiVisibility);
            setRequestedOrientation(this.mOriginalOrientation);
            this.mCustomViewCallback.onCustomViewHidden();
            this.mCustomViewCallback = null;
        }

        public void onShowCustomView(View paramView, WebChromeClient.CustomViewCallback paramCustomViewCallback)
        {
            if (this.mCustomView != null)
            {
                onHideCustomView();
                return;
            }
            this.mCustomView = paramView;
            this.mOriginalSystemUiVisibility = getWindow().getDecorView().getSystemUiVisibility();
            this.mOriginalOrientation = getRequestedOrientation();
            this.mCustomViewCallback = paramCustomViewCallback;
            ((FrameLayout)getWindow().getDecorView()).addView(this.mCustomView, new FrameLayout.LayoutParams(-1, -1));
            getWindow().getDecorView().setSystemUiVisibility(3846 | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
        }
    }

 @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        mWebView.saveState(outState);
    }

    @Override
    protected void onRestoreInstanceState(Bundle savedInstanceState) {
        super.onRestoreInstanceState(savedInstanceState);
        mWebView.restoreState(savedInstanceState);
    }
}

Di AndroidManifest.xml

<activity
  android:name=".MainActivity"
  android:configChanges="orientation|screenSize" />

Sumber Monster Techno

Manohar Reddy
sumber
Menambahkan android: configChanges = "orientasi | screenSize" bekerja dengan sempurna untuk saya. Terima kasih untuk bantuannya!!!
Burak
Menyetel WebChromeClient khusus memang memecahkan masalah bagi saya. Terima kasih!
vato
5

Sunting: silakan lihat jawaban saya yang lain , karena Anda mungkin tidak membutuhkan ini sekarang.

Seperti yang Anda katakan, di API level 11+, HTML5VideoFullScreen $ VideoSurfaceView diteruskan. Tapi menurut saya Anda tidak benar ketika Anda mengatakan bahwa "itu tidak memiliki MediaPlayer".

Ini adalah cara untuk menjangkau instance MediaPlayer dari instance HTML5VideoFullScreen $ VideoSurfaceView menggunakan refleksi :

@SuppressWarnings("rawtypes")
Class c1 = Class.forName("android.webkit.HTML5VideoFullScreen$VideoSurfaceView");
Field f1 = c1.getDeclaredField("this$0");
f1.setAccessible(true);

@SuppressWarnings("rawtypes")
Class c2 = f1.getType().getSuperclass();
Field f2 = c2.getDeclaredField("mPlayer");
f2.setAccessible(true);

Object ___html5VideoViewInstance = f1.get(focusedChild); // Look at the code in my other answer to this same question to see whats focusedChild

Object ___mpInstance = f2.get(___html5VideoViewInstance); // This is the MediaPlayer instance.

Jadi, sekarang Anda dapat menyetel listener onCompletion dari instance MediaPlayer seperti ini:

OnCompletionListener ocl = new OnCompletionListener()
{
    @Override
    public void onCompletion(MediaPlayer mp)
    {
        // Do stuff
    }
};

Method m1 = f2.getType().getMethod("setOnCompletionListener", new Class[] { Class.forName("android.media.MediaPlayer$OnCompletionListener") });
m1.invoke(___mpInstance, ocl);

Kode tidak gagal tetapi saya tidak sepenuhnya yakin apakah listener onCompletion itu benar-benar akan dipanggil atau dapat berguna untuk situasi Anda. Tapi kalau-kalau ada yang mau mencobanya.

cprcrack
sumber
1
Terima kasih atas bantuan Anda, saya mencoba kode ini - pertama-tama saya dapat melihat bahwa tidak semua perangkat menggunakan VideoSurfaceView entah bagaimana - HTC One X memiliki VideoTextureView yang mungkin merupakan kelas khusus mereka (bukan di dokumen android). Saya mencobanya di perangkat lain dan di salah satu perangkat tersebut pendengar penyelesaian dipanggil.
nbtk
Saya terbuka dengan opsi, saya ingin memutar video youtube pada aktivitas yang sama. Apa yang saya lakukan?
asok Buzz
Hai, saya mendapatkan pengecualian saat video menjadi layar penuh pada beberapa devides stacktrace Di Sini pastebin.com/9Gn9jmc2 Jika saya melakukan sesuatu yang salah
Rajnish Mishra
@cprcrack apakah mungkin untuk memaksa mengubah orientasi aktivitas dari potret ke lanskap saat layar penuh diklik pada video?
Muhammad
1

Terima kasih banyak untuk kelas itu, Cristian.

Saya membuat sedikit perubahan sehingga tampilan pemuatan kustom bersifat opsional, seperti:

  @Override
    public View getVideoLoadingProgressView() // Video will start loading, only called in the case of VideoView (typically API level 10-)
    {
        if (loadingView == null)
        {
            return super.getVideoLoadingProgressView();
        }
        else
        {
            loadingView.setVisibility(View.VISIBLE);
            return loadingView;
        }
    }

Saya juga menambahkan konstruktor baru yang hanya membutuhkan dua parameter. Bagaimanapun, hanya penyederhanaan kecil jika Anda tidak memerlukan tampilan pemuatan. Sekali lagi terima kasih telah memberikan ini.

Scott Schafer
sumber
1

Setel saja
mWebView.setWebChromeClient(new WebChromeClient());

dan pemutaran video seperti biasa tidak memerlukan tampilan khusus.

Pranav
sumber
7
Sayangnya, tidak membantu dalam kasus saya
resource8218
0

Ini bagus. Tetapi jika Anda ingin tautan situs web Anda terbuka di aplikasi itu sendiri, tambahkan kode ini di ExampleActivity.java Anda:

webView.setWebViewClient(new WebViewClient() {
        @Override
        public boolean shouldOverrideUrlLoading(WebView view, String url) {
            if (Uri.parse(url).getHost().endsWith("yourwebsite.com")) {
                return false;
            }

            Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
            view.getContext().startActivity(intent);
            return true;
        }
    });
Sid
sumber
0

Jawaban Cprcrack bekerja dengan sangat baik untuk API level 19 ke bawah. Hanya sedikit tambahan untuk cprcrack onShowCustomViewakan membuatnya berfungsi pada API level 21+

if (Build.VERSION.SDK_INT >= 21) {
      videoViewContainer.setBackgroundColor(Color.BLACK);
      ((ViewGroup) webView.getParent()).addView(videoViewContainer);
      webView.scrollTo(0,0);  // centers full screen view 
} else {
      activityNonVideoView.setVisibility(View.INVISIBLE);
      ViewGroup.LayoutParams vg = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
                    ViewGroup.LayoutParams.MATCH_PARENT);
      activityVideoView.addView(videoViewContainer,vg);
      activityVideoView.setVisibility(View.VISIBLE);
}

Anda juga perlu mencerminkan perubahan dalam onHideCustomView

Grux
sumber
0

Tampaknya di lollipop dan yang lebih baru (atau mungkin hanya Versi WebView yang berbeda) cprcrack's onHideCustomView()metode pemanggilan tidak berfungsi. Ini berfungsi jika dipanggil dari tombol keluar layar penuh tetapi ketika Anda secara khusus memanggil metode itu hanya akan keluar dari layar penuh tetapi webViewtetap kosong. Cara mengatasinya adalah dengan menambahkan baris kode ini ke onHideCustomView():

String js = "javascript:";
js += "var _ytrp_html5_video = document.getElementsByTagName('video')[0];";
js += "_ytrp_html5_video.webkitExitFullscreen();";
webView.loadUrl(js);

Ini akan memberi tahu webView bahwa layar penuh telah keluar.

Michael Kern
sumber