pyjnius webview

3,460 views
Skip to first unread message

Milos Gradjanin

unread,
Dec 2, 2013, 7:07:31 AM12/2/13
to kivy-...@googlegroups.com
I'm trying to mimic this:

WebView wv = (WebView) findViewById(R.id.webview);
wv.getSettings().setJavaScriptEnabled(true);
wv.setBackgroundColor(Color.TRANSPARENT);
String html = "<html><body style='margin:0;padding:0;'><script type='text/javascript' src='http://ad.leadboltads.net/show_app_ad.js?section_id=ID_HERE'></script></body></html>";   
wv.loadData(html, "text/html", "utf-8");

doing it like this:

WebView = autoclass('android.webkit.WebView')
wv = WebView()
wv.getSettings().setJavaScriptEnabled(True)
wv.setBackgroundColor(Color.TRANSPARENT);
html = "<html><body style='margin:0;padding:0;'><script type='text/javascript' src='http://ad.leadboltads.net/show_app_ad.js?section_id=ID_HERE'></script></body></html>"   
wv.loadData(html, "text/html", "utf-8")

but I'm getting this error:

jnius.jnius.JavaException: no constructor matching your arguments

What is the right way to implement this?
Message has been deleted

Milos Gradjanin

unread,
Dec 2, 2013, 7:16:19 AM12/2/13
to kivy-...@googlegroups.com
Here's the log:

[ImagePygame ] Load </data/data/org.test.testapp/files/lib/python2.7/site-packages/kivy/data/../data/images/defaulttheme-0.png>
I/python  (  929):  Traceback (most recent call last):
I/python  (  929):    File "/home/.../.buildozer/android/app/main.py", line 65, in <module>
I/python  (  929):    File "/home/.../.buildozer/android/platform/python-for-android/build/python-install/lib/python2.7/site-packages/kivy/app.py", line 577, in run
I/python  (  929):    File "/home/.../.buildozer/android/app/main.py", line 29, in build
I/python  (  929):    File "jnius_export_class.pxi", line 147, in jnius.jnius.JavaClass.__init__ (jnius/jnius.c:12809)
I/python  (  929):    File "jnius_export_class.pxi", line 199, in jnius.jnius.JavaClass.call_constructor (jnius/jnius.c:13645)
I/python  (  929):  jnius.jnius.JavaException: No constructor matching your arguments
I/python  (  929): Python for android ended.
I/ActivityManager(  192): Process org.test.testapp:python (pid 929) has died.

Doug Linder

unread,
Dec 2, 2013, 7:58:30 PM12/2/13
to kivy-...@googlegroups.com
You can't just suddenly create new views in android, which is what you're trying to do there.

Webviews are attached in the layout xml and then found and loaded by the findViewById() call.

I'm a little skeptical that this is even possible; the NDK guys have been trying to create webviews for 'pure' NDK applications since forever with no luck afaik.

~
Doug.

Mathieu Virbel

unread,
Dec 3, 2013, 1:46:45 AM12/3/13
to kivy-...@googlegroups.com
It is possible :)

But you need to carefully read the documentation. You cannot create
simply a Webview with an empty constructor, the documentation state that
you need to pass a context. In our case, that would be the instance of
the activity.

WebView = autoclass('android.webkit.WebView')
LayoutParams = autoclass('android.view.ViewGroup$LayoutParams')
activity = autoclass('org.renpy.android.PythonActivity').mActivity

# and then:
webview = WebView(activity)
activity.addContentView(clock, LayoutParams(-1, -1))

In my example, you need to add them in a Runnable. You can use our
android.runnable module for that:

from android.runnable import run_on_ui_thread

@run_on_ui_thread
def create_and_add_webview():
... do your stuff here ...


Here is my tests: https://gist.github.com/tito/6118172
As well as another example on how to handle an Android widget as a Kivy
widget, applied for the Camera android object:

https://github.com/tito/android-zbar-qrcode/

Or an example on integrate Google maps into Kivy:

https://github.com/tito/kivy-gmaps

Both instanciate and use native android widget :)

Enjoy,

Mathieu


Le 03/12/2013 01:58, Doug Linder a �crit :
> --
> You received this message because you are subscribed to the Google
> Groups "Kivy users support" group.
> To unsubscribe from this group and stop receiving emails from it, send
> an email to kivy-users+...@googlegroups.com.
> For more options, visit https://groups.google.com/groups/opt_out.

Milos Gradjanin

unread,
Dec 3, 2013, 6:17:39 AM12/3/13
to kivy-...@googlegroups.com
Great, I'm glad it's possible!

I'm gonna try and meddle with it now, thanks for the example, Tito.

Milos Gradjanin

unread,
Dec 4, 2013, 11:37:26 AM12/4/13
to kivy-...@googlegroups.com
It works perfectly. I see I'm gonna have to cover some python and android documentation to understand how and why of some things that are new to me. But out of the box, this works. Yay.

Milos Gradjanin

unread,
Dec 4, 2013, 11:54:59 AM12/4/13
to kivy-...@googlegroups.com
Here's how I put it:
...
from android.runnable import run_on_ui_thread

WebView = autoclass('android.webkit.WebView')
LayoutParams = autoclass('android.view.ViewGroup$LayoutParams')
activity = autoclass('org.renpy.android.PythonActivity').mActivity

class Wv(Widget):
    def __init__(self, **kwargs):
        super(Wv, self).__init__(**kwargs)
        self.create_webview()
    
    @run_on_ui_thread
    def create_webview(self):
        webview = WebView(activity)
        activity.addContentView(webview, LayoutParams(-1, -1))
        webview.getSettings().setJavaScriptEnabled(True)
        #having some trouble with this one: webview.setBackgroundColor(Color.TRANSPARENT)

        html = "<html><body style='margin:0;padding:0;'>\
            <script type='text/javascript'\
            src='http://ad.leadboltads.net/show_app_ad.js?section_id=ID_HERE'>\
            </script></body></html>"    
    
        activity.setContentView(webview)
        webview.loadData(html, "text/html", "utf-8")
        layout = LinearLayout(activity)
        layout.addView(activity.mView)
        activity.setContentView(layout)

...

Michael Hines

unread,
Jun 10, 2014, 3:36:46 AM6/10/14
to kivy-...@googlegroups.com
How do I instantiate this in a simple Kivy "main.py" file?

I tried doing:

class MyApp(App) :
    def build(self) :
        return Wv()

but, the app just crashes when trying to create the webview widget.

Should native android widgets be nested in another widget or something in order to run?

- Michael

Alexander Taylor

unread,
Jun 10, 2014, 5:55:55 AM6/10/14
to kivy-...@googlegroups.com
I thought I replied to this but now I don't see the answer...well, either way, the answer is that android widgets aren't (and *can't*) be part of kivy's normal widget tree. They will only be drawn in front of or behind kivy's entire window. I don't know how to manage this, but you could look at (for instance) tito's kivy-gmaps example.

Michael Hines

unread,
Jun 10, 2014, 11:36:39 AM6/10/14
to kivy-...@googlegroups.com
I don't think anyone cares that it actually integrates with the normal widget tree. There have been enough messages on this list asking for an integrated web browser, and this is the next best thing:

So, after enough trial and error, here's a usable example for all the googlers out there: Finally. A full web browser inside a native Kivy android app. (I'm not so interested in portability. That can be done later.)

import kivy                                                                                     
from kivy.app import App                                                                        
from kivy.lang import Builder                                                                   
from kivy.utils import platform                                                                 
from kivy.uix.widget import Widget                                                              
from kivy.clock import Clock                                                                    
from jnius import autoclass                                                                     
from android.runnable import run_on_ui_thread                                                   
                                                                                                
WebView = autoclass('android.webkit.WebView')                                                   
WebViewClient = autoclass('android.webkit.WebViewClient')                                       
activity = autoclass('org.renpy.android.PythonActivity').mActivity                              
                                                                                                
class Wv(Widget):                                                                               
    def __init__(self, **kwargs):                                                               
        super(Wv, self).__init__(**kwargs)                                                      
        Clock.schedule_once(self.create_webview, 0)                                             
                                                                                                
    @run_on_ui_thread                                                                           
    def create_webview(self, *args):                                                            
        webview = WebView(activity)                                                             
        webview.getSettings().setJavaScriptEnabled(True)                                        
        wvc = WebViewClient();                                                                  
        webview.setWebViewClient(wvc);                                                          
        activity.setContentView(webview)                                                        
        webview.loadUrl('http://www.google.com')
                                                                                                
class ServiceApp(App):                                                                          
    def build(self):                                                                            
        return Wv()                                                                             
                                                                                                
if __name__ == '__main__':                                                                      
    ServiceApp().run()

Ben Rousch

unread,
Jun 12, 2014, 9:42:15 PM6/12/14
to kivy-...@googlegroups.com

Awesome!

--
You received this message because you are subscribed to the Google Groups "Kivy users support" group.
To unsubscribe from this group and stop receiving emails from it, send an email to kivy-users+...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Michael R. Hines

unread,
Jun 13, 2014, 12:56:26 AM6/13/14
to kivy-...@googlegroups.com

@Alexander Taylor is right though, if you want to go beyond
this simple example, you will have to read (and understand)
Tito's gmaps example on github.

You have to intercept the touch events and determine whether you
are trying to interact with the webview or whether you are trying
to interact with the kivy object tree - that's no simple task.

- Michael

ZenCODE

unread,
Jun 14, 2014, 2:22:49 PM6/14/14
to kivy-...@googlegroups.com
@Micheal Hines

That totally rocks! And will really help many people. I've going to add it to the Snippets gallery in the wiki and link to it from the "Targetting Android" section of the docs. Do I just credit "Micheal Hines", or can I add links back your other projects (assuming you have any ;-))

Thanks

Michael R. Hines

unread,
Jun 14, 2014, 9:53:10 PM6/14/14
to kivy-...@googlegroups.com
Oh, no credit necessary...... Do post the sample, though. =)

Let's hope someone can expand on it
and make it more sophisticated - preferably a more
general Kivy widget that could be customized with different
resolutions and so forth.

- Michael
> --
> You received this message because you are subscribed to a topic in the
> Google Groups "Kivy users support" group.
> To unsubscribe from this topic, visit
> https://groups.google.com/d/topic/kivy-users/9sH0y3KxCuU/unsubscribe.
> To unsubscribe from this group and all its topics, send an email to

ZenCODE

unread,
Jun 15, 2014, 2:55:18 AM6/15/14
to kivy-...@googlegroups.com, mic...@hinespot.com
On the wiki: https://github.com/kivy/kivy/wiki/Android-native-embedded-browser

Added to the docs: https://github.com/kivy/kivy/commit/03505470d3df659596d87901f024ac49afc7206a

Personally I think the most important first step would be able to interact with Javapscript events. The browser engine itself is plenty powerful, and once you can catch/fire those events, you can build html/javascript that is fully interactive with your kivy environment.

But still, this is a great first step. Thanks :-)

Cheers

prajo...@gmail.com

unread,
Jul 27, 2014, 4:57:08 PM7/27/14
to kivy-...@googlegroups.com
How to run the webview on an underlay (behind kivy layer)?

Michael R. Hines

unread,
Aug 21, 2014, 4:14:39 AM8/21/14
to ZenCODE, kivy-...@googlegroups.com
Hi Folks,

I'm back with the same solution for iOS - I have a working Kivy webview on iOS now. Yay.

First: There is a caveat, for which I could use help from the kivy authors: I cannot seem to run the webview simultaneously with SDL. The only way to get the webview started from python was to initiate a function call from pyobjus into ObjectiveC to create a new window that contains the webview. But without disabling SDL, the window simply did not appear. Of course I don't *want* to disable it, but it works until someone with more expertise can figure out how to run them both at the same time.

Anyway, here's the steps:

1. First, open the xcode project and remove all the "*SDL*.a" libraries from the xcode project in the "Build Phases" section under "Link with Libraries" towards the bottom.
2. Find the main.m and main.h files and remove them. Because we have temporarily removed SDL, we no longer have a UIApplicationMain that was provided by Kivy-ios, so we have to make one from scratch and *then* call python main.py after the UI framework has started up. (Hopefully, someone else can recommend a better way)
3. Make a new main.h that looks like this: https://github.com/hinesmr/mica-ios/blob/master/main.h
4. Make a new main.m that looks like this: https://github.com/hinesmr/mica-ios/blob/master/main.m

You'll notice that this looks much like the original kivy-ios template, except that we have brought back the UIApplicationMain main routine startup.

6. Next open the kivy-generated file "bridge.h" and add this to the class:

@property (strong, nonatomic) UIWindow *window;

5. Next, open the kivy-generated file "bridge.m" in your xcode project and add the below new functions (we will call these from python using pyobjus).

#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
@implementation mica {

}
- (id) init {
if(self = [super init]) {
  self.window = nil;          // add this line to init function
  }
  return self;
}
- (void) dealloc {
  [self.window release];   // add this line to dealloc function
  [super dealloc];
}

/* Add these two new functions */

- (void) webviewstatic :(NSString*)html {
   [[NSOperationQueue mainQueue] addOperationWithBlock:^{
   if (self.window != nil) {
      [self.window release];
   }
   self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
   NSLog(@"Creating static webview splash.");  
   self.window.backgroundColor = [UIColor whiteColor];
   [self.window makeKeyAndVisible];
   UIWebView * webView = [[UIWebView alloc] initWithFrame: self.window.bounds];
   [webView loadHTMLString:html baseURL:nil];
   UIViewController *vc = [[UIViewController alloc] init];
   self.window.rootViewController = vc;
   [vc.view addSubview:webView];
  }];
}
- (void) webview :(NSString*)fullURL {
   [[NSOperationQueue mainQueue] addOperationWithBlock:^{
   if (self.window != nil) {
     [self.window release];
   } 
   self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
   NSLog(@"Creating webview. with url %@", fullURL);
   self.window.backgroundColor = [UIColor whiteColor];
   [self.window makeKeyAndVisible];
   NSURL * url = [NSURL URLWithString:fullURL];
   NSURLRequest * requestObj = [NSURLRequest requestWithURL:url];
   UIWebView * webView = [[UIWebView alloc] initWithFrame: self.window.bounds];
   [webView loadRequest:requestObj];
   UIViewController *vc = [[UIViewController alloc] init];
   self.window.rootViewController = vc;
   [vc.view addSubview:webView];
   }];
}

6. Finally, create a main.py file, but don't include anything from kivy (because we deleted SDL).
    We just want to make sure we can successfully invoke the webview from python.

7. In your main.py file, you should be able to do something like this:

from pyobjus import autoclass, objc_f, objc_str as String
bridge= autoclass('bridge').alloc().init()
bridge.webview_(String("http://www.google.com"))
bridge.webviewstatic_(String("hello world webpage"))


That's it!

- Michael

ZenCODE

unread,
Aug 21, 2014, 12:56:38 PM8/21/14
to kivy-...@googlegroups.com
Great stuff! And thanks for sharing ;-)

JR

unread,
Oct 3, 2014, 2:34:21 PM10/3/14
to kivy-...@googlegroups.com, zenkey....@gmail.com, mic...@hinespot.com
 A far more easier solution for ios is to just use pyobjus (Tested with ios 8.0):


from pyobjus import autoclass
from pyobjus.objc_py_types import NSRect,NSPoint,NSSize
from kivy.core.window import Window
from kivy.properties import  ObjectProperty
from kivy.uix.button import Button
from kivy.app import App

class MyApp(App):
    webbrowser = ObjectProperty(None)
    def hide_browser(self):
self.webbrowser.setHidden_(True)
    def unhide_browser(self):
self.webbrowser.setHidden_(False)
    def init_url(self,url):
webbrowser_url=autoclass('NSURL').alloc().initWithString_(url)
return autoclass('NSURLRequest').alloc().initWithURL_(webbrowser_url)
    def change_url(self,url):
req = self.init_url(url)
self.webbrowser.loadRequest_(req)
    def open_website(self,*args):
        self.change_url("http://www.google.de")
        self.unhide_browser()
    def build(self):

        #webbrowser will be painted over the existing app; 
        #size is divided by two due to retina display; 
        # to leave space for a close Button or other Widgets next to the Webbrowser change size and origin here.
        webbrowser_size = NSRect(NSPoint(0,0),NSSize(Window.size[0]/2.,Window.size[1]/2.))

        self.webbrowser = autoclass('UIWebView').alloc().initWithFrame_(webbrowser_size)
        self.hide_browser()
        view_controller = autoclass('UIApplication').sharedApplication().keyWindow.rootViewController.view()
        view_controller.addSubview_(self.webbrowser)

        button = Button(text="Open Webbrowser")
        button.bind(on_press=self.open_website)
        return button 



Reply all
Reply to author
Forward
0 new messages