Reputation: 2018
I have a WebView, which loads an html page from server, but the page is invisible on the WebView. The html is loaded properly (I've debugged with chrome://inspect and the html, including all javascripts exist), but it is invisible on the phone screen. There was no changes in my code, when this bug appeared. The bug appeared when I installed updates to Android System WebView on my phone. If I uninstall the updates, all works properly again. In addition, I've checked the callbacks of the WebViewClient and noticed that onPageCommitVisible is not called. So somehow, the page is not loaded properly. Only if I press the Back button, to exit the WebView, I see that the onPageCommitVisible is called for my webpage (buat it doesn't help, as the back button exists the WebView, as expected).
Here is my code for the webview:
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
RelativeLayout rlMain = new RelativeLayout(getActivity());
rlMain.setContentDescription(Constants.STARTAPP_AD_MAIN_LAYOUT_CONTENT_DESCRIPTION);
rlMain.setId(Constants.MAIN_LAYOUT_ID);
getActivity().setContentView(rlMain);
// Create WebView and set its parameters
try{
webView = new WebView(getActivity().getApplicationContext());
webView.setBackgroundColor(0xFF000000);
getActivity().getWindow().getDecorView().findViewById(android.R.id.content).setBackgroundColor(0x00777777);
webView.setVerticalScrollBarEnabled(false);
webView.setHorizontalScrollBarEnabled(false);
webView.getSettings().setJavaScriptEnabled(true);
webView.setWebChromeClient(new WebChromeClient());
// set software acceleration
if (softwareAcceleration) {
ApiUtil.setWebViewLayerTypeSoftware(webView, null);
}
webView.setOnLongClickListener(new OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
return true;
}
});
webView.setLongClickable(false);
webView.addJavascriptInterface(createJsInterface(), Constants.INTERFACE);
setWebViewSpecificParameters(webView);
webView.loadDataWithBaseURL("http://www.xxxxxx.com", getHtml(), "text/html", "utf-8", null);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
WebView.setWebContentsDebuggingEnabled(true);
}
webView.setWebViewClient(new MyWebViewClient());
RelativeLayout.LayoutParams webviewPrms = new RelativeLayout.LayoutParams(
RelativeLayout.LayoutParams.MATCH_PARENT,
RelativeLayout.LayoutParams.MATCH_PARENT
);
rlMain.addView(webView, webviewPrms);
}
public void setWebViewSpecificParameters(final WebView webView) {
webView.setOnTouchListener(new View.OnTouchListener() {
public boolean onTouch(View v, MotionEvent event) {
return (event.getAction() == MotionEvent.ACTION_MOVE);
}
});
}
private class MyWebViewClient extends WebViewClient {
@Override
public WebResourceResponse shouldInterceptRequest(WebView view, String url) {
Logger.log(TAG, Log.DEBUG, "!!!!!shouldInterceptRequest" );
return super.shouldInterceptRequest(view, url);
}
@Override
public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) {
Logger.log(TAG, Log.DEBUG, "!!!!!shouldInterceptRequest" );
return super.shouldInterceptRequest(view, request);
}
@Override
public void onPageFinished(WebView view, String url) {
setWebViewBackground(view);
runJavascript(Constants.JAVASCRIPT_SET_MODE_SERVER, getPosition());
runJavascript(Constants.JAVASCRIPT_ENABLE_SCHEME, "externalLinks");
InterstitialMode.this.onWebviewPageFinished();
}
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
return clicked(url);
}
}
Upvotes: 5
Views: 4571
Reputation: 218
This is entire code with loader in Composable. In @TomTasche solution we need one more if to not trigger methods onPageFinished
with no sense.
@Composable
fun WebViewContent(link: String) {
var isLoading by remember { mutableStateOf(true) }
var onPageStartedCalled by remember { mutableStateOf(false) }
val scope = rememberCoroutineScope()
val context = LocalContext.current
Box(
modifier = Modifier
.fillMaxSize(),
contentAlignment = Alignment.Center
) {
AndroidView(
factory = {
WebView(context).apply {
layoutParams = ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT
)
var wasCommitCalled = false
webViewClient = object : WebViewClient() {
override fun shouldOverrideUrlLoading(
view: WebView?,
url: String?
): Boolean {
return if (view != null && url != null) {
checkShouldOverrideUrlLoading(view, url)
} else false
}
override fun onPageStarted(view: WebView?, url: String?, favicon: Bitmap?) {
super.onPageStarted(view, url, favicon)
wasCommitCalled = false
onPageStartedCalled = true
isLoading = true
}
override fun onPageFinished(view: WebView?, url: String?) {
if (onPageStartedCalled) {
scope.launch {
delay(1500)
if (!wasCommitCalled) {
if (url != null) {
loadUrl(url)
}
}
isLoading = false
}
}
}
override fun onPageCommitVisible(view: WebView?, url: String?) {
super.onPageCommitVisible(view, url)
wasCommitCalled = true
}
}
settings.setSupportZoom(true)
settings.builtInZoomControls = true
settings.displayZoomControls = false
settings.javaScriptEnabled = true
settings.useWideViewPort = true
loadUrl("https://drive.google.com/viewerng/viewer?embedded=true&url=$link")
this.setInitialScale(1)
}
},
modifier = Modifier
.fillMaxSize()
.then(Modifier.visibility(!isLoading))
)
LoaderContent(
isLoading = isLoading
)
}
}
@Composable
fun Modifier.visibility(isVisible: Boolean): Modifier {
return if (isVisible) this else alpha(0f)
}
fun checkShouldOverrideUrlLoading(webView: WebView, url: String): Boolean {
webView.loadUrl(url)
if (url.endsWith(".pdf") || url.endsWith(".xlsx") || url.endsWith(".xls") || url.endsWith(".doc") || url.endsWith(
".docx"
) || url.endsWith(".ppt") || url.endsWith(".pptx")
) {
webView.loadUrl("https://docs.google.com/gview?embedded=true&url=$url")
return true
}
return false
}
Upvotes: 0
Reputation: 5526
In an app that navigates from one website to another based on user input, there's a high chance (>50%) that the second website won't display at all after calling WebView.loadUrl
and the previous website stays visible - it's even interactive, i.e. scrolling works. The problem is usually resolved after calling WebView.loadUrl
again. There's no obvious indication of the bug occurring other than the user not seeing the second website. Relying on the user to reload the page manually is not a satisfying solution since the bug occurs quite often.
I was able to workaround this issue by using a custom WebViewClient
to detect that the second website was not loaded properly and triggering a reload:
setWebViewClient(new WebViewClient() {
@Override
public void onPageFinished(WebView view, String url) {
super.onPageFinished(view, url);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
buggyWebViewHandler.postDelayed(new Runnable() {
@Override
public void run() {
if (!wasCommitCalled) {
loadUrl(url);
}
}
}, 2500);
}
}
@Override
public void onPageCommitVisible(WebView view, String url) {
wasCommitCalled = true;
}
});
Where buggyWebViewHandler
is a Handler or any other class that allows deferring a piece of code for some time. Additionally, you'll need to set wasCommitCalled = false;
whenever WebView.loadUrl
is called - for example by overriding the method.
Note that this only works for Android 23 onwards because that's when onPageCommitVisible
was added. See here for a full implementation: https://github.com/TomTasche/OpenDocument.droid/blob/8c2eec5c57e5962e9ac4c46549be2241b259eb32/app/src/main/java/at/tomtasche/reader/ui/widget/PageView.java#L72-L96
If anyone is brave enough to dig deeper into why this is happening: while debugging it seemed that onPageStarted is not called whenever this bug occurs. Maybe that helps...
Upvotes: 3