Lecture d'une vidéo HTML5 en plein écran dans la vue Web Android


92

Eh bien, je cherche déjà depuis quelques jours, comment afficher une vidéo HTML5 en mode plein écran sur Android WebView.

J'ai réussi à lire des vidéos HTML5 sur ma vue Web. Des problèmes surviennent lors de l'affichage de la vidéo en mode plein écran.

Comme je l'ai compris, Android dispose de deux façons de gérer la balise <video>:

  1. Sur les versions Android <= 2.3.3 , la méthode onShowCustomView est déclenchée, et je peux avoir l'instance VideoView, et définir les auditeurs lorsque la vidéo est terminée, définir les contrôleurs, etc.

  2. Sur ICS (et probablement 3.0 et supérieur) , il semble que la <vidéo> soit gérée de manière différente. Lorsque la vidéo HTML5 est lue, onShowCustomView n'est pas appelée en mode normal - il semble qu'il y ait une entreprise interne à l'intérieur de WebView qui lit la vidéo, et toutes les commandes définies dans la balise <video> sont affichées - I ne peut en aucun cas y accéder. En fait, si la vidéo est lue en mode normal, c'est OK car les commandes sont là et fonctionnent.

Cela m'a conduit au gros problème: lors de l'affichage de la vidéo en mode plein écran, onShowCustomView est appelé - mais sur ICS, le paramètre «view» n'est pas une instance de VideoView.

J'ai réussi à découvrir que l'instance est de VideoSurfaceView, une classe interne privée de la classe HTML5VideoFullScreen. La seule façon d'accéder à cette classe interne est la réflexion.

Après avoir examiné GrepCode pour cette classe, j'ai appris que contrairement à VideoView, le HTML5VideoFullScreen $ VideoSurfaceView ne contient pas d'instance MediaPlayer que je peux écouter ses événements ou accéder à ses contrôles. La seule chose que je peux faire est de prendre ce VideoSurfaceView tel quel et de le placer dans une mise en page plein écran sans le contrôler.

Bottom line - Lors de l'affichage de la vidéo en plein écran, je ne sais pas quand la vidéo se termine, ses commandes ne sont pas affichées - c'est assez triste. Je ne parviens pas à obtenir le déclencheur pour fermer le plein écran.

J'ai essayé quelques solutions de contournement infructueuses:

  1. Réflexion: J'ai essayé d'atteindre l'instance HTML5VideoFullScreen, qui contient un membre MediaPlayer, à partir de la classe interne VideoSurfaceView. Je n'ai pas réussi à l'obtenir, je ne suis pas sûr que cela soit possible (ViewSurfaceView ne contient pas l'instance de son propriétaire).

  2. Inscrivez-vous aux événements vidéo via Javascript (onended, par exemple), et gérez ce dont j'ai besoin en JAVA via JavascriptInterface: J'ai trouvé que cette solution n'était pas fiable car en faisant cela j'ai rencontré un autre problème: la balise <video> peut être imbriquée dans un. La source iframe n'est pas la mienne et je ne peux pas obtenir son contenu (getElementById ou getElementsByTagName [i] sont des valeurs nulles) - ce qui signifie que je ne peux pas atteindre l'élément <video> dans l'iframe.

Je cherche toujours une solution, très peu est écrit sur ce problème. Quelqu'un a-t-il réussi à le résoudre? Une aide serait très appréciée!

Classe VideoView : Ici (avec MediaPlayer)

Classe HTML5VideoFullScreen $ VideoSurfaceView : Ici (n'a pas MediaPlayer)


Même problème que vous et arrivé aux mêmes conclusions également. J'ai travaillé là-dessus pendant environ 20 heures, mais je n'abandonnerai pas au moins 20 heures de plus (de vraies heures de travail, je veux dire). Je vous dirai si je trouve la solution. En attendant, comme cette question a été posée il y a 20 jours, essayez-vous toujours de la résoudre ou avez-vous appliqué une autre solution de contournement?
cprcrack

Au fait, un rapide coup d'œil au code source de HTML5VideoFullScreen a montré que sa classe parent HTML5VideoView contient un MediaPlayer.
cprcrack

Réponses:


176

Edit 2014/10: à la demande générale, je maintiens et déplace cela sur GitHub. Veuillez vérifier cprcrack / VideoEnabledWebView pour la dernière version. Gardera cette réponse uniquement à titre de référence.

Edit 2014/01: utilisation d'exemple améliorée pour inclure les vues nonVideoLayout, videoLayout et videoLoading, pour les utilisateurs qui demandent plus d'exemples de code pour une meilleure compréhension.

Edit 2013/12: quelques corrections de bugs liés à la compatibilité des appareils Sony Xperia, mais qui affectaient en fait tous les appareils.

Edit 2013/11: après la sortie d'Android 4.4 KitKat (API niveau 19) avec sa nouvelle webview Chromium, j'ai dû travailler à nouveau dur. Plusieurs améliorations ont été apportées. Vous devriez mettre à jour vers cette nouvelle version. Je publie cette source sous WTFPL .

Edit 2013/04: après 1 semaine de travail acharné, j'ai enfin réalisé tout ce dont j'avais besoin. Je pense que ces deux classes génériques que j'ai créées peuvent résoudre tous vos problèmes.

VideoEnabledWebChromeClientpeut être utilisé seul si vous n'avez pas besoin de la fonctionnalité qui VideoEnabledWebViewajoute. Mais VideoEnabledWebViewdoit toujours compter sur un VideoEnabledWebChromeClient. Veuillez lire attentivement tous les commentaires des deux classes.

VideoEnabledWebChromeClient Classe

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 Classe

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

}

Exemple d'utilisation:

Disposition principale activity_main.xml dans laquelle nous mettons un VideoEnabledWebView et d'autres vues utilisées:

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

Activity's onCreate () , dans lequel on l'initialise:

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

Et n'oubliez pas d'appeler 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
Ce que vous avez suggéré, notifiera lorsque la vidéo se termine uniquement sur l'API <= 10. Sur> = 11, rien n'enregistre le onCompletionListener ...
nbtk

4
Merci pour vos efforts! Mais si la <video> est sous un <iframe>, nous ne pouvons pas l'atteindre, donc la partie javacsript n'est pas parfaite. Votre code est très professionnel, impressionnant.
nbtk

3
L'intégration du code de cette réponse stackoverflow.com/questions/20379478/… résoudra le problème que j'ai posé ci-dessus.
Wienke Giezeman

2
Besoin de mise à niveau, ne fonctionne pas sur Android v4.4.4 +, veuillez ajouter une option au jeu, passez automatiquement en plein écran. Vous pouvez implémenter vimeo sur ce code? Merci d'avoir partagé le code sur github. @cprcrack
Floride

2
cela ne fonctionne pas dans les dernières versions adroid ... ci-dessus 5.0, 6.0,7.0
UMAR-MOBITSOLUTIONS

11

Testé sur la version Android 9.0

Aucune des réponses n'a fonctionné pour moi. C'est la dernière chose qui a fonctionné

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

Dans AndroidManifest.xml

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

Source Monster Techno


L'ajout d' android: configChanges = "orientation | screenSize" a parfaitement fonctionné pour moi. Merci pour l'aide!!!
Burak

La configuration du WebChromeClient personnalisé a résolu le problème pour moi. Merci!
vato le

5

Edit: s'il vous plaît voir mon autre réponse , car vous n'en avez probablement pas besoin maintenant.

Comme vous l'avez dit, dans les niveaux d'API 11+, un HTML5VideoFullScreen $ VideoSurfaceView est passé. Mais je ne pense pas que vous ayez raison de dire que "il n'a pas de MediaPlayer".

Voici le moyen d'accéder à l'instance MediaPlayer à partir de l'instance HTML5VideoFullScreen $ VideoSurfaceView à l'aide de la réflexion :

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

Vous pouvez maintenant définir l'écouteur onCompletion de l'instance MediaPlayer comme ceci:

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

Le code n'échoue pas mais je ne suis pas complètement sûr si cet écouteur onCompletion sera vraiment appelé ou s'il pourrait être utile à votre situation. Mais juste au cas où quelqu'un voudrait l'essayer.


1
Merci pour votre aide J'ai essayé ce code - d'abord, je peux voir que tous les appareils n'utilisent pas VideoSurfaceView - HTC One X a VideoTextureView qui est probablement leur classe personnalisée (pas dans la documentation Android). Je l'ai essayé sur d'autres appareils et sur aucun d'entre eux, l'écouteur d'achèvement a été appelé.
nbtk

Je suis ouvert avec l'option, je veux lire une vidéo youtube sur la même activité. Que fais-je?
asok Buzz le

Salut, je reçois une exception lorsque la vidéo passe en plein écran sur certains devides stacktrace est ici pastebin.com/9Gn9jmc2 Si je fais quelque chose de mal
Rajnish Mishra

@cprcrack est-il possible de forcer le changement d'orientation de l'activité de portrait en paysage lorsque le plein écran est cliqué sur la vidéo?
Muhammad

1

Merci beaucoup pour ce cours, Cristian.

J'ai apporté une modification mineure afin que la vue de chargement personnalisée soit facultative, comme ceci:

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

J'ai également ajouté un nouveau constructeur qui ne prend que deux paramètres. Quoi qu'il en soit, juste une petite simplification si vous n'avez pas besoin de la vue de chargement. Merci encore d'avoir fourni cela.


1

Juste réglé
mWebView.setWebChromeClient(new WebChromeClient());

et la vidéo est lue comme normalement n'a pas besoin de vue personnalisée.


7
Malheureusement, n'a pas aidé dans mon cas
resource8218

0

C'est bien. Mais si vous souhaitez que les liens de votre site Web s'ouvrent dans l'application elle-même, ajoutez ce code dans votre 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

La réponse de Cprcrack fonctionne très bien pour les niveaux d'API 19 et moins. Juste un ajout mineur à cprcrack le onShowCustomViewfera fonctionner au niveau de l'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);
}

Vous devrez également refléter les changements dans onHideCustomView


0

Il semble que dans Lollipop et plus (ou peut-être juste une version WebView différente) cette cprcrack's onHideCustomView()méthode d' appel ne fonctionne pas. Cela fonctionne s'il est appelé à partir du bouton de sortie plein écran, mais lorsque vous appelez spécifiquement la méthode, il ne quittera que le plein écran mais webViewreste vide. Une façon de contourner le problème consiste simplement à ajouter ces lignes de code à onHideCustomView():

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

Cela informera le WebView que le plein écran est sorti.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.