diff --git a/plugin.xml b/plugin.xml index 011d8c009..506c5ef3e 100644 --- a/plugin.xml +++ b/plugin.xml @@ -48,6 +48,8 @@ + + diff --git a/src/android/InAppBrowser.java b/src/android/InAppBrowser.java index b3e0e6126..58069505d 100644 --- a/src/android/InAppBrowser.java +++ b/src/android/InAppBrowser.java @@ -18,15 +18,18 @@ Licensed to the Apache Software Foundation (ASF) under one */ package org.apache.cordova.inappbrowser; +import android.app.Activity; import android.annotation.SuppressLint; import android.annotation.TargetApi; import android.content.ComponentName; import android.content.Context; import android.content.Intent; +import android.content.pm.ActivityInfo; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.os.Parcelable; import android.provider.Browser; +import android.os.Handler; import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.drawable.Drawable; @@ -97,6 +100,7 @@ public class InAppBrowser extends CordovaPlugin { private static final String EXIT_EVENT = "exit"; private static final String LOCATION = "location"; private static final String ZOOM = "zoom"; + private static final String FULL_SCREEN = "fullscreen"; private static final String HIDDEN = "hidden"; private static final String LOAD_START_EVENT = "loadstart"; private static final String LOAD_STOP_EVENT = "loadstop"; @@ -134,6 +138,7 @@ public class InAppBrowser extends CordovaPlugin { private boolean clearSessionCache = false; private boolean hadwareBackButton = true; private boolean mediaPlaybackRequiresUserGesture = false; + private boolean fullScreenFeature = false; private boolean shouldPauseInAppBrowser = false; private boolean useWideViewPort = true; private ValueCallback mUploadCallback; @@ -670,6 +675,10 @@ public String showWebPage(final String url, HashMap features) { if (mediaPlayback != null) { mediaPlaybackRequiresUserGesture = mediaPlayback.equals("yes") ? true : false; } + String fullScreen = features.get(FULL_SCREEN); + if (fullScreen != null) { + fullScreenFeature = fullScreen.equals("yes") ? true : false; + } String cache = features.get(CLEAR_ALL_CACHE); if (cache != null) { clearAllCache = cache.equals("yes") ? true : false; @@ -727,6 +736,9 @@ public String showWebPage(final String url, HashMap features) { // Create dialog in new thread Runnable runnable = new Runnable() { + + private boolean inFullScreen = false; + /** * Convert our DIP units to Pixels * @@ -740,6 +752,38 @@ private int dpToPixels(int dipValue) { return value; } + + private void enableFullScreen(Activity activity, Window window) { + inFullScreen = true; + WindowManager.LayoutParams attrs = window.getAttributes(); + attrs.flags |= WindowManager.LayoutParams.FLAG_FULLSCREEN; + attrs.flags |= WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON; + window.setAttributes(attrs); + if (android.os.Build.VERSION.SDK_INT >= 14) + { + //noinspection all + int flags = View.SYSTEM_UI_FLAG_LOW_PROFILE; + if (android.os.Build.VERSION.SDK_INT >= 16) + { + flags = View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_IMMERSIVE; + } + window.getDecorView().setSystemUiVisibility(flags); + } + + } + + private void disableFullScreen(Activity activity, Window window) { + inFullScreen = false; + WindowManager.LayoutParams attrs = window.getAttributes(); + attrs.flags &= ~WindowManager.LayoutParams.FLAG_FULLSCREEN; + attrs.flags &= ~WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON; + window.setAttributes(attrs); + if (android.os.Build.VERSION.SDK_INT >= 14) + { + //noinspection all + window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_VISIBLE); + } + } private View createCloseButton(int id) { View _close; @@ -798,8 +842,30 @@ public void run() { }; // Let's create the main dialog - dialog = new InAppBrowserDialog(cordova.getActivity(), android.R.style.Theme_NoTitleBar); + dialog = new InAppBrowserDialog(cordova.getActivity(), android.R.style.Theme_NoTitleBar_Fullscreen); dialog.getWindow().getAttributes().windowAnimations = android.R.style.Animation_Dialog; + + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) { + final View decor = dialog.getWindow().getDecorView(); + decor.setOnSystemUiVisibilityChangeListener (new View.OnSystemUiVisibilityChangeListener() { + public void onSystemUiVisibilityChange(int visibility) { + new Handler().postDelayed(new Runnable() { + public void run(){ + if (inFullScreen) + { + decor.setSystemUiVisibility(View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_IMMERSIVE); + } + } + }, 3000); + } + }); + } + + if (fullScreenFeature) + { + enableFullScreen(cordova.getActivity(), dialog.getWindow()); + } + dialog.requestWindowFeature(Window.FEATURE_NO_TITLE); if (fullscreen) { dialog.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); @@ -808,8 +874,12 @@ public void run() { dialog.setInAppBroswer(getInAppBrowser()); // Main container layout - LinearLayout main = new LinearLayout(cordova.getActivity()); - main.setOrientation(LinearLayout.VERTICAL); + RelativeLayout main = new RelativeLayout(cordova.getActivity()); + RelativeLayout fullScreenMain = new RelativeLayout(cordova.getActivity()); + fullScreenMain.setLayoutParams(new RelativeLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)); + LinearLayout browserMain = new LinearLayout(cordova.getActivity()); + browserMain.setOrientation(LinearLayout.VERTICAL); + browserMain.setLayoutParams(new RelativeLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)); // Toolbar layout RelativeLayout toolbar = new RelativeLayout(cordova.getActivity()); @@ -937,11 +1007,11 @@ public boolean onKey(View v, int keyCode, KeyEvent event) { // WebView - inAppWebView = new WebView(cordova.getActivity()); + inAppWebView = new VideoEnabledWebView(cordova.getActivity()); inAppWebView.setLayoutParams(new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)); inAppWebView.setId(Integer.valueOf(6)); // File Chooser Implemented ChromeClient - inAppWebView.setWebChromeClient(new InAppChromeClient(thatWebView) { + InAppChromeClient inAppChromeClient = new InAppChromeClient(thatWebView, browserMain, fullScreenMain) { // For Android 5.0+ public boolean onShowFileChooser (WebView webView, ValueCallback filePathCallback, WebChromeClient.FileChooserParams fileChooserParams) { @@ -982,7 +1052,33 @@ public void openFileChooser(ValueCallback uploadMsg, String acceptType) cordova.startActivityForResult(InAppBrowser.this, Intent.createChooser(content, "Select File"), FILECHOOSER_REQUESTCODE); } + }; + + inAppChromeClient.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: + + Activity activity = cordova.getActivity(); + Window window = dialog.getWindow(); + if (fullscreen) + { + enableFullScreen(activity, window); + activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE); + } + else + { + if (!fullScreenFeature) + { + disableFullScreen(activity, window); + } + activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT); + } + } }); + inAppWebView.setWebChromeClient(inAppChromeClient); + currentClient = new InAppBrowserClient(thatWebView, edittext, beforeload); inAppWebView.setWebViewClient(currentClient); WebSettings settings = inAppWebView.getSettings(); @@ -1060,13 +1156,13 @@ public void postMessage(String data) { // Don't add the toolbar if its been disabled if (getShowLocationBar()) { // Add our toolbar to our main view/layout - main.addView(toolbar); + browserMain.addView(toolbar); } // Add our webview to our main view/layout RelativeLayout webViewLayout = new RelativeLayout(cordova.getActivity()); webViewLayout.addView(inAppWebView); - main.addView(webViewLayout); + browserMain.addView(webViewLayout); // Don't add the footer unless it's been enabled if (showFooter) { @@ -1078,6 +1174,9 @@ public void postMessage(String data) { lp.width = WindowManager.LayoutParams.MATCH_PARENT; lp.height = WindowManager.LayoutParams.MATCH_PARENT; + main.addView(browserMain); + main.addView(fullScreenMain); + if (dialog != null) { dialog.setContentView(main); dialog.show(); diff --git a/src/android/InAppChromeClient.java b/src/android/InAppChromeClient.java index fe5dd3495..45e5e7699 100644 --- a/src/android/InAppChromeClient.java +++ b/src/android/InAppChromeClient.java @@ -24,6 +24,8 @@ Licensed to the Apache Software Foundation (ASF) under one import org.json.JSONArray; import org.json.JSONException; +import android.view.View; +import android.view.ViewGroup; import android.webkit.JsPromptResult; import android.webkit.WebChromeClient; import android.webkit.WebStorage; @@ -31,14 +33,14 @@ Licensed to the Apache Software Foundation (ASF) under one import android.webkit.WebViewClient; import android.webkit.GeolocationPermissions.Callback; -public class InAppChromeClient extends WebChromeClient { +public class InAppChromeClient extends VideoEnabledWebChromeClient { private CordovaWebView webView; private String LOG_TAG = "InAppChromeClient"; private long MAX_QUOTA = 100 * 1024 * 1024; - public InAppChromeClient(CordovaWebView webView) { - super(); + public InAppChromeClient(CordovaWebView webView, View activityNonVideoView, ViewGroup activityVideoView) { + super(activityNonVideoView, activityVideoView); this.webView = webView; } /** diff --git a/src/android/VideoEnabledWebChromeClient.java b/src/android/VideoEnabledWebChromeClient.java new file mode 100644 index 000000000..89dcea340 --- /dev/null +++ b/src/android/VideoEnabledWebChromeClient.java @@ -0,0 +1,294 @@ +package org.apache.cordova.inappbrowser; + +import android.media.MediaPlayer; +import android.view.SurfaceView; +import android.view.View; +import android.view.ViewGroup; +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 MediaPlayer.OnPreparedListener, MediaPlayer.OnCompletionListener, MediaPlayer.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 not attached to 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 not attached to 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). + */ + @SuppressWarnings("unused") + 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 + */ + @SuppressWarnings("unused") + 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 + if (activityNonVideoView != null) + { + activityNonVideoView.setVisibility(View.INVISIBLE); + } + + if (activityVideoView != null) + { + activityVideoView.addView(videoViewContainer, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.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 + if (activityVideoView != null) + { + activityVideoView.setVisibility(View.INVISIBLE); + activityVideoView.removeView(videoViewContainer); + } + + if (activityNonVideoView != null) { + 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) + */ + @SuppressWarnings("unused") + public boolean onBackPressed() + { + if (isVideoFullscreen) + { + onHideCustomView(); + return true; + } + else + { + return false; + } + } + +} diff --git a/src/android/VideoEnabledWebView.java b/src/android/VideoEnabledWebView.java new file mode 100644 index 000000000..600750a24 --- /dev/null +++ b/src/android/VideoEnabledWebView.java @@ -0,0 +1,139 @@ +package org.apache.cordova.inappbrowser; + +import android.annotation.SuppressLint; +import android.content.Context; +import android.os.Handler; +import android.os.Looper; +import android.util.AttributeSet; +import android.util.Log; +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 @SuppressWarnings("unused") + public void notifyVideoEnd() // Must match Javascript interface method of VideoEnabledWebChromeClient + { + Log.d("___", "GOT IT"); + // 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; + + @SuppressWarnings("unused") + 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) + */ + @SuppressWarnings("unused") + 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 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) + //noinspection all + addJavascriptInterface(new JavascriptInterface(), "_VideoEnabledWebView"); // Must match Javascript interface name of VideoEnabledWebChromeClient + + addedJavascriptInterface = true; + } + } + +}