Capture picture from WebView - android

Is it possible to listen for WebView page load full completed and then capture screenshot of loaded page?
I tried two ways, but they both not working:
using WebViewClient and onPageFinished(WebView view, String url).
It doesn't work (and it described in docs) because Picture may be not ready it this moment
using WebView.PictureListener and onNewPicture(WebView view, Picture picture)
It also doesn't solve problem, because this method calls undetermined times. At first time, picture often contains only part of page content. And I don't know way how determine, that the current call is last for the page.
I want to notice, that the problem is to find right moment to make screenshot, and not how it can be done.

[Note that since I answered this, onNewPicture has been deprecated (see http://developer.android.com/reference/android/webkit/WebView.PictureListener.html and What does "This method is deprecated" mean for application developers). Unfortunately, there is no information on what replaces it, or at what API levels it was supported. I guess that means that you use this at your own risk.]
I think you’re correct that onNewPicture is the right place to capture a screenshot of a loaded WeView page, but also correct that it is hard to know when to actually do the capture.
It appears that WebView calls onNewPicture whenever there has been any drawing. For example, it calls onNewPicture repeatedly when the search bar is in keyboard entry mode and the cursor is flicking. Similarly, for some web pages (eg www.yelp.com/nyc) it calls onNewPicture repeatedly, even after the page has finished drawing, probably because of the flashing cursor in the Search box. But at the other extreme it will call onNewPicture only once (eg if the user drops down an iGoogle item).
So there’s no easy rule? The approach we’ve taken is to
monitor a range of events that are involved / affect page loading – such as shouldOverrideUrl, onPageFinished, focus changes, scrolling start/end, in addition to onNewPicture
run a timer (2secs is working well) on onNewPicture, reset by a new onNewPicture
implement a page loading FSM which uses the events and timer expiry as inputs, and moves through a series of state/action transitions, to the point where it decides it genuinely has a new picture.
Not pretty, but it works, with very few cases where it captures the same picture twice – and no cases where it fails to capture a picture where it should.

Related

Android webview doesn't finish rendering text

I'm developing an application that requires somewhat complex html rendering (basically text and images, but the text may have some advanced features like custom font and path) and I'm using webviews to render the content.
The problem is that some of the pages doesn't render text in some devices.
I'm using all three callbacks:
onPageFinished;
onProgressChanged;
onNewPicture (deprecated, but some users related that it works)
but even if they're triggered I get a WebView that is missing the text in most of the pages. On some devices when I touch the webview the text appears, but it's an erratic behaviour that can't be trusted.
I also tried called invalidate() constantly but it doesn't force the text to draw. I'm logging the webview console and apparently it's not throwing any error (notice that sometimes the text is rendered perfectly and the problem occurs for pages that rendered correctly before). I'm configuring my webview like (I know that some methods are deprecated, but some other solutions related that it worked for then):
offScreenWebView.layout(0, 0, viewPortWidth, viewPortHeight);
offScreenWebView.getSettings().setUseWideViewPort(true);
offScreenWebView.getSettings().setLoadWithOverviewMode(true);
offScreenWebView.getSettings().setTextZoom(100);
offScreenWebView.setDrawingCacheEnabled(true);
offScreenWebView.getSettings().setJavaScriptEnabled(true);
offScreenWebView.getSettings().setRenderPriority(WebSettings.RenderPriority.HIGH);
offScreenWebView.getSettings().setPluginState(WebSettings.PluginState.ON_DEMAND);
if(Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP) {
offScreenWebView.enableSlowWholeDocumentDraw();
}
I'm at loose now, because I've no clue why the text doesn't render and it seems I can't force it to render.
I've managed to somewhat solve my problem. The main issue I was having was due to the webviews were being loaded simultaneously (they're used at a viewpager) and probably this incurred in some concurrency problem when the webviews were loading the font. I encapsulated the URL loading and ensured that the webviews were loaded one page at a time.
Also, when destroying the webviews I couldn't call any method like cancelTimer, this resulted in problems when rendering other pages with similar font. The only thing I did was to point the references to null.
It's somewhat slow but at least it was the only way to get consistent rendering results using the webviews.
This is probably due to the latest update to Android System Web View, that comes automatically from Google Play. If you uninstall the update, it helps. It is of course not a really good solution((((
Another workaround is disable HW accelleration in the webview

Is it possible to change the context of a WebView after it has been instantiated?

I have a WebView I'm loading in an activity in order to have it preloaded so that it pops up immediately in a different Activity (launched from the first).
The problem is that in order to instantiate a WebView, I have to pass in a Context, in this case it's the first mentioned above.
So it works great, and the second Activity shows the WebView just fine. The problem is that if I click a <select> dropdown in the WebView, its selector dialog shows up UNDER the WebView. It feels like the select doesn't work at all until you hit the back button and briefly see the selection dialog just before you return to the parent activity.
It seems as though when I append the WebView to the layout in the second activity, it's modals get attached to that activity's window, but the WebView itself is attached to the parent activity's window, so it shows in a higher point in the hierarchy.
How can I possibly change the Context of the WebView after it's been instantiated?
This is a very difficult problem to solve -- I have to create the WebViews before the activity is started, but I also need the selection dialogs to work.
Please if anyone can give me some insights here I'd greatly appreciate it.
This is for an SDK project, so I will not have access to the parent activity. Also, saveState isn't working, because the bulk of what is shown in the WebView is generated by JavaScript, and the full DOM stack doesn't transfer.
You can try to create the WebView with a MutableContextWrapper:
MutableContextWrapper mMutableContext=new MutableContextWrapper(context);
WebView mWebView=new WebView(mMutableContext);
and later on you could do
mMutableContext.setBaseContext(newcontext);
But ...
WebView is a very complex component that will probably be using the passed context to create other objects like Handlers. WebView probably uses those handlers to post stuff to the original UI thread, so at the end you'll probably have a View with a mix of contexts, you know, a double memory leak (if it ever works properly)
Webview spans at least 1 thread "webcore" that is where the action happens and is also in constant communication with the original UI thread with ... handlers? through the original context? who knows!
There are even 2 different webview engines: Kitkat is chromium-based while jelly bean and previous versions use AOSP/WebView. So you have an additional breaking point.
The reasons you state are not strong enough imho. WebView is not that slow. If the app you load is, try to optimize it. There are a lot of things you can do for that, like loading the HTML & graphics from internal assets.
In my App (it's browser) I have the same problem. I don't like to load WebView every time when user back to App. And I've solved this problem partially. I've overridden onBackPressed() on my HomeActivity and use moveTaskToBack(true) instead of super.onBackPressed(). So when user use system back on HomeActivity it does't destroy Activity and all views. It just minimize the App. Visually it's the same behavior but if user try to run App by launch icon, all views already loaded. I know it's temporary solution and all views can be destroyed by system any time but it gives quite good result. And covers a lot of cases for me.

jQuery Mobile: should I use pageBeforeShow or pageBeforeChange for last adjustments?

I'm developing a mobile app in jQueryMobile and PhoneGap. Often, due to the nature of jQM or because it's about loading data, a page will need some last-minute adjustments before it is shown. For example, form fields need to be filled in with dynamically retrieved data, or the contents of list items need to be given a slightly different style in order to fit better.
I am currently doing all these adjustments using the pageBeforeShow event handler. But I wonder if I shouldn't have been using the pageBeforeChange event handler. The jQM docs do not really make it clear how these two events relate to each other, i.e. which is fired first.
What I know
I do know the following
pageBeforeShow and pageShow are triggered after all of jQM's markup (e.g. making list items look pretty) has been applied.
pageBeforeShow and pageShow are bound to a specific page, whereas pageChange is called whenever a change of page occurs (so if you want to make specifi changes to one page before the user gets there, you need to test the event.toPage property)
Why I'm asking
And this is the background why I want to know if page(Before)Change is a better candidate.
jQuery Mobile page transitions are awkward on many devices. The big issue on Android devices is that page changes are jumpy: regardless of the transition type (fade, pop, etc), the page being left by the user will pop back into view briefly after the new page has more or less finished in the browser.
In my experience, this occurs mostly when other animations are running or are started while the page transition takes place. Basically, the Android browser doesn't seem to want to apply transitions to elements that are not actually in view, and it will flip back and forth between jQM pages as a result.
I've already developed a workaround where I delay any markup and form adjustments for a page by about 1000ms, which prevents the flashing but does mean that the user may be seeing these adjustments happen on screen after the page has come into view.
This analysis makes me think that pageBeforeChange might be a better candidate for attaching any markup and form adjustments. But it will be a big rewrite of the code, and I don't know what unforeseen stuff I will be getting into. Anyone have any experience with these events?
I'm not sure pagebeforechange would be the best place to do this. I don't see pagebeforechange as a page-level event, but more of a site-level event. I use it mainly if I want to take over navigation or to build dynamic pages.
I use pageinit when I want to attach event handlers to a page. I use pagebeforeshow when I want to change the contents or look of a page before it is shown.
Hope this helps.

Trigger or Force Redraw or Re-Render of WebView

My understanding is that WebView's PictureListener.onNewPicture() is called whenever the already loaded page finished rendering (or re-rendering in case certain events make the WebKit engine re-calculate page layout).
I also (think that I) understand that those WebView renders are controlled internally in WebView and ordinarily I wouldn't care how they are being generated.
However, for a certain function in my app, I need to trigger an extra PictureListener.onNewPicture() without re-loading the url (i.e. no reload() or loadUrl()).
Essentially, what I am looking for is a function like repaint() or redraw() or some other mechanism that only generate one more PictureListener.onNewPicture() and that's it (without the slowness and overhead associated with re-loading data).
Is there a way to achieve this?
Try using the requestLayout() function.
not sure if it will do what you want, but it worth a try.

onClick event in android webview too slow

I've got the feeling that javascript itself is pretty fast in an andorid webview, but there is a long delay between touching an element and the onclick event beeing fired.
I could imagine that this is a feature if you navigate between pages - you first see the highlight on the element, and then you see the effect (navigation). But for applications, this is too slow.
Is there a way to change this behavior?
Or is there maybe another event I should go for, like an onHover or onTouch? Something which fires way before the onClick?
This is a known 'issue' related to 300ms due to user tapping/zooming at display:
http://updates.html5rocks.com/2013/12/300ms-tap-delay-gone-away
In the near future, this seems to be solved, in static sized layouts, adding information at header. But the actual webkit webview version does not have/consider this option.
In my case, the solution was use Tappy lib:
https://github.com/filamentgroup/tappy
Just import it, and bind to each element that has an click event, and you will see the difference.
Just one more point: I adjusted the timer to 100000ms (default:1000ms) to avoid event repetition in some specific cases.
I guess I've found the answer...
If you take a look at jQuery mobile, you'll find a tap event. This works instantly - exactly as I need it.
Looking deeper into the source, I've found that the tap event uses a mousedown ....

Categories

Resources