package com.mopub.mobileads; import android.content.Context; import android.graphics.Color; import android.graphics.PixelFormat; import android.os.Build.VERSION; import android.os.Build.VERSION_CODES; import android.support.annotation.NonNull; import android.view.Gravity; import android.view.WindowManager; import android.webkit.WebSettings; import android.webkit.WebView; import com.mopub.common.util.VersionCode; import com.mopub.common.util.Views; import com.mopub.mobileads.util.WebViews; public class BaseWebView extends WebView { private static boolean sDeadlockCleared = false; protected boolean mIsDestroyed; public BaseWebView(Context context) { /* * Important: don't allow any WebView subclass to be instantiated using * an Activity context, as it will leak on Froyo devices and earlier. */ super(context.getApplicationContext()); enablePlugins(false); WebViews.setDisableJSChromeClient(this); if (!sDeadlockCleared) { clearWebViewDeadlock(getContext()); sDeadlockCleared = true; } } protected void enablePlugins(final boolean enabled) { // Android 4.3 and above has no concept of plugin states if (VersionCode.currentApiLevel().isAtLeast(VersionCode.JELLY_BEAN_MR2)) { return; } if (enabled) { getSettings().setPluginState(WebSettings.PluginState.ON); } else { getSettings().setPluginState(WebSettings.PluginState.OFF); } } @Override public void destroy() { mIsDestroyed = true; // Needed to prevent receiving the following error on Android versions using WebViewClassic // https://code.google.com/p/android/issues/detail?id=65833. Views.removeFromParent(this); // Even after removing from the parent, WebViewClassic can leak because of a static // reference from HTML5VideoViewProcessor. Removing children fixes this problem. removeAllViews(); super.destroy(); } @Deprecated // for testing void setIsDestroyed(boolean isDestroyed) { mIsDestroyed = isDestroyed; } /** * This fixes https://code.google.com/p/android/issues/detail?id=63754, * which occurs on KitKat devices. When a WebView containing an HTML5 video is * is destroyed it can deadlock the WebView thread until another hardware accelerated WebView * is added to the view hierarchy and restores the GL context. Since we need to use WebView * before adding it to the view hierarchy, this method clears the deadlock by adding a * separate invisible WebView. * * This potential deadlock must be cleared anytime you attempt to access a WebView that * is not added to the view hierarchy. */ private void clearWebViewDeadlock(@NonNull final Context context) { if (VERSION.SDK_INT == VERSION_CODES.KITKAT) { // Create an invisible webview final WebView webView = new WebView(context.getApplicationContext()); webView.setBackgroundColor(Color.TRANSPARENT); // For the deadlock to be cleared, we must load content and add to the view hierarchy. Since // we don't have an activity context, we'll use a system window. webView.loadDataWithBaseURL(null, "", "text/html", "UTF-8", null); final WindowManager.LayoutParams params = new WindowManager.LayoutParams(); params.width = 1; params.height = 1; // Unlike other system window types TYPE_TOAST doesn't require extra permissions params.type = WindowManager.LayoutParams.TYPE_TOAST; params.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED; params.format = PixelFormat.TRANSPARENT; params.gravity = Gravity.START | Gravity.TOP; final WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); windowManager.addView(webView, params); } } }