Odtwarzanie wideo HTML5 na pełnym ekranie w przeglądarce internetowej w systemie Android


92

Cóż, szukałem już od kilku dni, jak wyświetlić wideo HTML5 w trybie pełnoekranowym na Android WebView.

Udało mi się odtwarzać filmy HTML5 na moim widoku internetowym. Podczas wyświetlania wideo w trybie pełnoekranowym pojawiają się problemy.

Jak się domyśliłem, Android ma dwa sposoby obsługi tagu <video>:

  1. W wersjach Androida <= 2.3.3 uruchamiana jest metoda onShowCustomView i mogę mieć instancję VideoView i ustawić odbiorniki po zakończeniu wideo, ustawić kontrolery itp. Jak dotąd dobrze.

  2. W ICS (i prawdopodobnie 3.0 i nowszych) wygląda na to, że <video> jest obsługiwane w inny sposób. Kiedy wideo HTML5 jest odtwarzane, onShowCustomView nie jest wywoływany w trybie normalnym - wygląda na to, że wewnątrz WebView znajduje się wewnętrzna firma, która odtwarza wideo i wszystkie kontrolki zdefiniowane w tagu <video> są wyświetlane - I nie mogę uzyskać do niego w żaden sposób dostępu. Właściwie, jeśli wideo jest odtwarzane w trybie normalnym, jest to w porządku, ponieważ elementy sterujące są dostępne i działają.

To doprowadziło mnie do dużego problemu: podczas wyświetlania wideo w trybie pełnoekranowym wywoływany jest onShowCustomView - ale w ICS parametr „view” nie jest instancją VideoView.

Udało mi się dowiedzieć, że instancja to VideoSurfaceView, prywatna klasa wewnętrzna klasy HTML5VideoFullScreen. Jedynym sposobem, w jaki możemy uzyskać dostęp do tej klasy wewnętrznej, jest odbicie.

Po przyjrzeniu się GrepCode dla tej klasy, dowiedziałem się, że w przeciwieństwie do VideoView, HTML5VideoFullScreen $ VideoSurfaceView nie zawiera instancji MediaPlayer, której mogę słuchać jej zdarzeń lub uzyskiwać dostęp do jej kontrolek. Jedyne, co mogę zrobić, to wziąć ten VideoSurfaceView tak, jak jest i umieścić go w układzie pełnoekranowym bez kontrolowania go.

Konkluzja - Podczas wyświetlania wideo na pełnym ekranie nie wiem, kiedy wideo się kończy, jego elementy sterujące nie są wyświetlane - jest to dość smutne. Nie mogę znaleźć spustu do zamknięcia pełnego ekranu.

Wypróbowałem kilka nieudanych obejść:

  1. Refleksja: próbowałem dotrzeć do instancji HTML5VideoFullScreen, która zawiera element członkowski MediaPlayer, z klasy wewnętrznej VideoSurfaceView. Nie udało mi się go zdobyć, nie jestem pewien, czy jest to możliwe (ViewSurfaceView nie posiada instancji swojego właściciela).

  2. Zarejestruj się na zdarzenia wideo przez Javascript (na przykład onended) i obsługuj to, czego potrzebuję z powrotem w JAVA przez JavascriptInterface: Okazało się, że to rozwiązanie nie jest niezawodne, ponieważ podczas wykonywania tego napotkałem inny problem: tag <video> może być zagnieżdżony w. Źródło iframe nie jest moje i nie mogę pobrać jego zawartości (getElementById lub getElementsByTagName [i] to wartości null) - co oznacza, że ​​nie mogę dotrzeć do elementu <video> wewnątrz elementu iframe.

Wciąż szukam rozwiązania, niewiele napisano na ten temat. Czy ktoś zdołał to rozwiązać? Pomoc będzie bardzo mile widziane!

Klasa VideoView : Tutaj (ma MediaPlayer)

HTML5VideoFullScreen $ VideoSurfaceView, klasa: Tutaj (nie ma MediaPlayer)


Ten sam problem, co ty i również doszedł do tych samych wniosków. Pracowałem nad tym około 20 godzin, ale nie poddam się jeszcze przez co najmniej 20 godzin (mam na myśli prawdziwe godziny pracy). Powie ci, jeśli znajdę rozwiązanie. W międzyczasie, ponieważ to pytanie zostało zadane 20 dni temu, czy nadal próbujesz go rozwiązać, czy też zastosowałeś jakieś inne obejście?
cprcrack

Przy okazji, szybkie spojrzenie na kod źródłowy HTML5VideoFullScreen pokazało, że jego klasa nadrzędna HTML5VideoView zawiera MediaPlayer.
cprcrack

Odpowiedzi:


177

Edytuj 2014/10: na popularne żądanie utrzymuję i przenoszę to do GitHub. Sprawdź ostatnią wersję cprcrack / VideoEnabledWebView . Zachowa tę odpowiedź tylko w celach informacyjnych.

Edytuj 2014/01: ulepszone przykładowe użycie, aby uwzględnić widoki nonVideoLayout, videoLayout i videoLoading, dla tych użytkowników, którzy żądają więcej przykładowego kodu w celu lepszego zrozumienia.

Edytuj 2013/12: kilka poprawek błędów związanych ze zgodnością urządzeń Sony Xperia, które w rzeczywistości dotyczyły wszystkich urządzeń.

Edycja 2013/11: po wydaniu Androida 4.4 KitKat (poziom API 19) z nowym widokiem na przeglądarkę internetową Chromium musiałem znowu ciężko pracować. Wprowadzono kilka ulepszeń. Należy zaktualizować do tej nowej wersji. Udostępniam to źródło pod WTFPL .

Edycja 2013/04: po 1 tygodniu ciężkiej pracy w końcu osiągnąłem wszystko, czego potrzebowałem. Myślę, że te dwie ogólne klasy, które stworzyłem, mogą rozwiązać wszystkie Twoje problemy.

VideoEnabledWebChromeClientmoże być używany samodzielnie, jeśli nie potrzebujesz funkcji, która VideoEnabledWebViewdodaje. Ale VideoEnabledWebViewzawsze musi polegać na VideoEnabledWebChromeClient. Prosimy o uważne przeczytanie wszystkich komentarzy obu zajęć.

VideoEnabledWebChromeClient, klasa

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, klasa

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

}

Przykładowe użycie:

Główny układ activity_main.xml, w którym umieściliśmy VideoEnabledWebView i inne używane widoki:

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

Aktywność onCreate () , w której ją inicjujemy:

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

I nie zapomnij zadzwonić do 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();
        }
    }
}

1
To, co zasugerowałeś, powiadomi, gdy wideo zakończy się tylko w API <= 10. On> = 11, nic nie rejestruje onCompletionListener ...
nbtk

4
Dziękuję za Twój wysiłek! Ale jeśli <video> znajduje się pod <iframe>, nie możemy do niego dotrzeć, więc część javacsript nie jest idealna. Twój kod jest bardzo profesjonalny, imponujący.
nbtk

3
Włączenie kodu tej odpowiedzi stackoverflow.com/questions/20379478/… rozwiąże problem, który przedstawiłem powyżej.
Wienke Giezeman

2
Potrzebujesz aktualizacji, nie działaj na Androidzie w wersji 4.4.4 +, dodaj opcję do gry, automatycznie przejdź do pełnego ekranu. Możesz zaimplementować vimeo na tym kodzie? Dzięki za udostępnienie kodu na githubie. @cprcrack
Floryda

2
nie działa w najnowszych wersjach adroidów ... powyżej 5.0, 6.0, 7.0
UMAR-MOBITSOLUTIONS

13

Testowane na Android 9.0 wersji

Żadna z odpowiedzi nie pomogła. To ostatnia rzecz, która zadziałała

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

W AndroidManifest.xml

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

Źródło: Monster Techno


Dodanie androida: configChanges = "orientacja | screenSize" działało idealnie. Dzięki za pomoc!!!
Burak

Ustawienie niestandardowego WebChromeClient rozwiązało problem za mnie. Dzięki!
vato

Dzięki! Pomogło mi również uruchomić UniteGallery w Android WebView.
Jan-Peter Schmidt

5

Edycja: zobacz moją drugą odpowiedź , ponieważ prawdopodobnie nie potrzebujesz jej teraz.

Jak powiedziałeś, na poziomach API 11+ przekazywany jest HTML5VideoFullScreen $ VideoSurfaceView. Ale myślę, że nie masz racji, mówiąc, że „nie ma odtwarzacza multimedialnego”.

Oto sposób na dotarcie do instancji MediaPlayer z instancji HTML5VideoFullScreen $ VideoSurfaceView przy użyciu refleksji :

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

Więc teraz możesz ustawić detektor onCompletion instancji MediaPlayer w następujący sposób:

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

Kod nie zawiedzie, ale nie jestem całkowicie pewien, czy ten detektor onCompletion zostanie naprawdę wywołany, czy może być przydatny w twojej sytuacji. Ale na wszelki wypadek, gdyby ktoś chciał tego spróbować.


1
Dzięki za pomoc. Wypróbowałem ten kod - najpierw widzę, że nie wszystkie urządzenia w jakiś sposób korzystają z VideoSurfaceView - HTC One X ma VideoTextureView, który jest prawdopodobnie ich niestandardową klasą (nie w dokumentacji Androida). Wypróbowałem to na innych urządzeniach i na żadnym z nich nie został wywołany odbiornik zakończenia.
nbtk

Otwieram się z opcją, chcę odtworzyć wideo z youtube na tej samej czynności. Co ja robię?
asok Buzz

Cześć, otrzymuję wyjątek, gdy wideo przechodzi w tryb pełnoekranowy na niektórych devides stacktrace jest tutaj pastebin.com/9Gn9jmc2 Jeśli robię coś źle
Rajnish Mishra

@cprcrack Czy można wymusić zmianę orientacji działania z pionowej na poziomą po kliknięciu pełnego ekranu wideo?
Muhammad

1

Dziękuję bardzo za te zajęcia, Cristian.

Dokonałem drobnych poprawek, aby niestandardowy widok ładowania był opcjonalny, na przykład:

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

Dodałem również nowy konstruktor, który przyjmuje tylko dwa parametry. W każdym razie tylko drobne uproszczenie, jeśli nie potrzebujesz widoku ładowania. Jeszcze raz dziękujemy za udostępnienie tego.


1

Po prostu ustaw
mWebView.setWebChromeClient(new WebChromeClient());

a wideo odtwarza się tak, jak zwykle, nie wymaga żadnego niestandardowego widoku.


7
Niestety, nie pomogło w moim przypadku
resource8218

0

To jest świetne. Ale jeśli chcesz, aby linki do Twojej witryny otwierały się w samej aplikacji, dodaj ten kod w swoim ExampleActivity.java:

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

0

Odpowiedź Cprcrack działa bardzo dobrze na poziomach API 19 i niższych. Wystarczy niewielki dodatek do cprcrack, onShowCustomViewaby działał na poziomie API 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);
}

Będziesz także musiał odzwierciedlić zmiany w onHideCustomView


0

Wygląda na to, że w Lollipop i nowszych (a może po prostu w innej wersji WebView) ta cprcrack's onHideCustomView()metoda wywoływania nie działa. Działa, jeśli jest wywoływana z przycisku wyjścia z trybu pełnoekranowego, ale po konkretnym wywołaniu metody wyjdzie tylko z pełnego ekranu, ale webViewpozostanie pusta. Aby obejść ten problem, wystarczy dodać te wiersze kodu do onHideCustomView():

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

To powiadomi webView o zamknięciu pełnego ekranu.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.