The WebView Code
from kivy.uix.modalview import ModalView
from kivy.clock import Clock
from android.runnable import run_on_ui_thread
from jnius import autoclass, cast, PythonJavaClass, java_method
WebViewA = autoclass('android.webkit.WebView')
WebViewClient = autoclass('android.webkit.WebViewClient')
LayoutParams = autoclass('android.view.ViewGroup$LayoutParams')
LinearLayout = autoclass('android.widget.LinearLayout')
KeyEvent = autoclass('android.view.KeyEvent')
ViewGroup = autoclass('android.view.ViewGroup')
DownloadManager = autoclass('android.app.DownloadManager')
DownloadManagerRequest = autoclass('android.app.DownloadManager$Request')
Uri = autoclass('android.net.Uri')
Environment = autoclass('android.os.Environment')
Context = autoclass('android.content.Context')
PythonActivity = autoclass('org.kivy.android.PythonActivity')
class DownloadListener(PythonJavaClass):
#https://stackoverflow.com/questions/10069050/download-file-inside-webview
__javacontext__ = 'app'
__javainterfaces__ = ['android/webkit/DownloadListener']
@java_method('(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;J)V')
def onDownloadStart(self, url, userAgent, contentDisposition, mimetype,
contentLength):
mActivity = PythonActivity.mActivity
context = mActivity.getApplicationContext()
visibility = DownloadManagerRequest.VISIBILITY_VISIBLE_NOTIFY_COMPLETED
dir_type = Environment.DIRECTORY_DOWNLOADS
uri = Uri.parse(url)
filepath = uri.getLastPathSegment()
request = DownloadManagerRequest(uri)
request.setNotificationVisibility(visibility)
request.setDestinationInExternalFilesDir(context,dir_type, filepath)
dm = cast(DownloadManager,
mActivity.getSystemService(Context.DOWNLOAD_SERVICE))
dm.enqueue(request)
class KeyListener(PythonJavaClass):
__javacontext__ = 'app'
__javainterfaces__ = ['android/view/View$OnKeyListener']
def __init__(self, listener):
super().__init__()
self.listener = listener
@java_method('(Landroid/view/View;ILandroid/view/KeyEvent;)Z')
def onKey(self, v, key_code, event):
if event.getAction() == KeyEvent.ACTION_DOWN and\
key_code == KeyEvent.KEYCODE_BACK:
return self.listener()
class WebView(ModalView):
# https://developer.android.com/reference/android/webkit/WebView
def __init__(self, url, enable_javascript = False, enable_downloads = False,
enable_zoom = False, width=None, height=None, **kwargs):
super().__init__(**kwargs)
self.url = url
self.enable_javascript = enable_javascript
self.enable_downloads = enable_downloads
self.enable_zoom = enable_zoom
self.webview = None
self.enable_dismiss = True
if width:
self.width = width
if height:
self.height = height
self.open()
@run_on_ui_thread
def on_open(self):
mActivity = PythonActivity.mActivity
webview = WebViewA(mActivity)
webview.setWebViewClient(WebViewClient())
webview.getSettings().setJavaScriptEnabled(self.enable_javascript)
webview.getSettings().setBuiltInZoomControls(self.enable_zoom)
webview.getSettings().setDisplayZoomControls(False)
webview.getSettings().setAllowFileAccess(True) #default False api>29
layout = LinearLayout(mActivity)
layout.setOrientation(LinearLayout.VERTICAL)
layout.addView(webview, self.width, self.height)
mActivity.addContentView(layout, LayoutParams(-1,-1))
webview.setOnKeyListener(KeyListener(self._back_pressed))
if self.enable_downloads:
webview.setDownloadListener(DownloadListener())
self.webview = webview
self.layout = layout
try:
webview.loadUrl(self.url)
except Exception as e:
print('Webview.on_open(): ' + str(e))
self.dismiss()
@run_on_ui_thread
def on_dismiss(self):
if self.enable_dismiss:
self.enable_dismiss = False
parent = cast(ViewGroup, self.layout.getParent())
if parent is not None: parent.removeView(self.layout)
self.webview.clearHistory()
self.webview.clearCache(True)
self.webview.clearFormData()
self.webview.destroy()
self.layout = None
self.webview = None
@run_on_ui_thread
def on_size(self, instance, size):
if self.webview:
params = self.webview.getLayoutParams()
params.width = self.width
params.height = self.height
self.webview.setLayoutParams(params)
def pause(self):
if self.webview:
self.webview.pauseTimers()
self.webview.onPause()
def resume(self):
if self.webview:
self.webview.onResume()
self.webview.resumeTimers()
def downloads_directory(self):
# e.g. Android/data/org.test.myapp/files/Download
dir_type = Environment.DIRECTORY_DOWNLOADS
context = PythonActivity.mActivity.getApplicationContext()
directory = context.getExternalFilesDir(dir_type)
return str(directory.getPath())
def _back_pressed(self):
if self.webview.canGoBack():
self.webview.goBack()
else:
self.dismiss()
return True
def closeWebView(self, force=False, animation=True):
self.dismiss(force=force, animation=animation)
Display WebView
class App(APP):
def build(self):
#self.manager = ScreenManager()
#login = Screen(name="login")
#login.add_widget(LoginPage())
#register = Screen(name="register")
#register.add_widget(Register())
#forgotLogin = Screen(name="forgot")
#forgotLogin.add_widget(ForgotLogin())
#chat = Screen(name="chat")
#chat.add_widget(Chat())
#self.manager.add_widget(login)
#self.manager.add_widget(register)
#self.manager.add_widget(forgotLogin)
#self.manager.add_widget(chat)
#self.manager.current = "login"
return WebView(url="https://google.com/", enable_javascript=True, enable_zoom=True)
if __name__== "__main__":
chat = App()
chat.run()
I compiled and tested on android 10 it force closes and after running adb logcat I get:
05-29 23:37:14.410 1760 4712 D PowerManagerServiceEx: releaseWakeLockInternal: lock=42843022 [WindowManager], flags=0x0
05-29 23:37:14.469 27595 27630 I python : Traceback (most recent call last):
05-29 23:37:14.469 27595 27630 I python : File "/home/mike/kiv/.buildozer/android/app/main.py", line 66, in <module>05-29 23:37:14.469 27595 27630 I python : File "/home/mike/kiv/.buildozer/android/platform/build-armeabi-v7a/build/python-installs/ga.animechat.chatapp/kivy/app.py", line 949, in run
05-29 23:37:14.470 27595 27630 I python : File "/home/mike/kiv/.buildozer/android/platform/build-armeabi-v7a/build/python-installs/ga.animechat.chatapp/kivy/app.py", line 919, in _run_prepare
05-29 23:37:14.470 27595 27630 I python : File "/home/mike/kiv/.buildozer/android/app/main.py", line 26, in build
05-29 23:37:14.470 27595 27630 I python : File "/home/mike/kiv/.buildozer/android/app/browse.py", line 98, in __init__
05-29 23:37:14.470 27595 27630 I python : File "/home/mike/kiv/.buildozer/android/platform/build-armeabi-v7a/build/python-installs/ga.animechat.chatapp/kivy/uix/modalview.py", line 222, in open
05-29 23:37:14.470 27595 27630 I python : File "/home/mike/kiv/.buildozer/android/platform/build-armeabi-v7a/build/python-installs/ga.animechat.chatapp/kivy/core/window/__init__.py", line 1305, in add_widget
05-29 23:37:14.470 27595 27630 I python : kivy.uix.widget.WidgetException: Cannot add <browse.WebView object at 0xbedc50d0> to window, it already has a parent <__main__.App object at 0xbf7f1258>
05-29 23:37:14.470 27595 27630 I python : Python for android ended.
05-29 23:37:14.521 27595 27622 E libEGL : validate_display:91 error 3008 (EGL_BAD_DISPLAY)
05-29 23:37:14.521 27595 27622 I chatty : uid=10479(ga.animechat.chatapp.ga.animechat.chatapp) imechat.chatapp identical 3 lines
05-29 23:37:14.521 27595 27622 E libEGL : validate_display:91 error 3008 (EGL_BAD_DISPLAY)
05-29 23:37:14.521 27595 27622 F OpenGLRenderer: Failed to set damage region on surface 0xe6e60e20, error=EGL_BAD_DISPLAY
05-29 23:37:14.521 27595 27622 F libc : Fatal signal 6 (SIGABRT), code -1 (SI_QUEUE) in tid 27622 (RenderThread), pid 27595 (imechat.chatapp)
I Don't know what to do I tried passing parent=self
it still gave the error. Any idea on what to do? Google doesn't mention the webview anywhere so hopefully you can point out a way to fix it. On the actual code the webview is in its seperate file not sure if that's the issue or not. Maybe I should wrap it in another class that inherits Widget
.