Hi there
How to create Webview with working FileChooser, and how to handle back button to return to main App.
Here is my code, Webview opens, it loads webpage, there is image back
button at the browser window but when i press it sometimes it return
back to main App, some times it close Webview and main App with only
error: #00 pc 00225c74 /data/app/org.test.myapp/lib/arm/libpython3.8.so
File chooser does not work!
`from kivy.uix.modalview import ModalView
from android.runnable import run_on_ui_thread
from jnius import autoclass, cast, PythonJavaClass, java_method, MetaJavaClass
WebViewA = autoclass('android.webkit.WebView')
WebViewClient = autoclass('android.webkit.WebViewClient')
WebChromeClient = autoclass('android.webkit.WebChromeClient')
WebSettings = autoclass('android.webkit.WebSettings')
LayoutParams = autoclass('android.view.ViewGroup$LayoutParams')
ButtonLayoutParams = autoclass('android.widget.FrameLayout$LayoutParams')
LinearLayout = autoclass('android.widget.LinearLayout')
Button = autoclass('android.widget.ImageButton')
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')
View = autoclass('android.view.View')
R = autoclass('android.R$drawable')
OnClickListener = autoclass('android/view/View$OnKeyListener')
HashMap = autoclass('java.util.HashMap')
String = autoclass('java.lang.String')
Color = autoclass('android.graphics.Color')
Intent = autoclass('android.content.Intent')
File = autoclass('java.io.File')
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 CustomWebChromeClient(WebChromeClient):
javaclass = 'android/webkit/WebChromeClient'
metaclass = MetaJavaClass
def onShowFileChooser(self, webView, filePathCallback, fileChooserParams):
# Handle file chooser request here
intent = Intent(Intent.ACTION_GET_CONTENT)
intent.addCategory(Intent.CATEGORY_OPENABLE)
intent.setType("*/*")
chooserIntent = Intent.createChooser(intent, "Choose a file")
PythonActivity.mActivity.startActivityForResult(chooserIntent, 1)
# Save the callback for handling the selected file
self.filePathCallback = filePathCallback
return True
def onActivityResult(self, requestCode, resultCode, intent):
if requestCode == 1:
if resultCode == -1: # RESULT_OK
# Get the selected file's URI and pass it to the WebView
selected_file_uri = intent.getData()
self.filePathCallback.onReceiveValue(selected_file_uri)
else:
# Handle the case where file selection was canceled by the user
self.filePathCallback.onReceiveValue(None)
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()
# Убрвть самодеятельность
return super(KeyListener, self).onKey(v, key_code, event)
class ImageButtonClickListener(PythonJavaClass):
javainterfaces = ['android/view/View$OnClickListener']
def init(self, listener):
super().init()
self.listener = listener
@java_method('(Landroid/view/View;)V')
def onClick(self, view):
try:
return self.listener()
except Exception as e:
print("error", e)
class WebView(ModalView):
# https://developer.android.com/reference/android/webkit/WebView
def __init__(self, screen_manager, url="", enable_raw_html=False, html="", enable_javascript=False, enable_downloads=False,
enable_zoom=False, **kwargs):
super().__init__(**kwargs)
self.url = url
self.screen_manager = screen_manager
self.enable_raw_html = enable_raw_html
self.html = html
self.enable_javascript = enable_javascript
self.enable_downloads = enable_downloads
self.enable_zoom = enable_zoom
self.webview = None
self.layout = None
self.enable_dismiss = True
self.headers = {
'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64; rv:60.0) Gecko/20100101 Firefox/111.0',
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
'Accept-Encoding': 'br',
'Authorization': f'Bearer {self.screen_manager.model.token}',
'Content-Type': 'application/json'
}
self.open()
def but_click_func(self, instance):
self._back_pressed()
@run_on_ui_thread
def on_open(self):
mActivity = PythonActivity.mActivity
webview = WebViewA(mActivity)
custom_webchromeclient = CustomWebChromeClient()
webview.setWebChromeClient(custom_webchromeclient)
webview.setWebViewClient(WebViewClient())
webview.getSettings().setLoadsImagesAutomatically(True)
webview.getSettings().setJavaScriptEnabled(self.enable_javascript)
webview.getSettings().setBuiltInZoomControls(self.enable_zoom)
webview.getSettings().setDisplayZoomControls(False)
webview.getSettings().setAllowFileAccess(True) # default False api>29
webview.getSettings().setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW)
webview.getSettings().setDomStorageEnabled(True)
webview.setWebContentsDebuggingEnabled(True)
layout = LinearLayout(mActivity)
layout.setOrientation(LinearLayout.VERTICAL)
layout.setBackgroundColor(Color.parseColor("#673ab7"))
iner_layout = LinearLayout(mActivity)
iner_layout.setOrientation(LinearLayout.VERTICAL)
iner_layout.setBackgroundColor(Color.parseColor("#673ab7"))
iner_layout.setPadding(75, 0, 0, 0)
layout.addView(iner_layout, self.width, 60)
# Add back button
back_button = Button(mActivity)
back_button.setImageResource(mActivity.getResources().getIdentifier('arrow_left_custom', 'drawable', mActivity.getPackageName()))
back_button.setBackgroundColor(0x00000000) # Set background to transparent
back_button.setPadding(0, 0, 0, 0) # Remove padding
back_button.setElevation(0) # Remove shadow
back_button.setBackground(None) # Remove default background
back_button.setOnClickListener(ImageButtonClickListener(self._back_pressed))
iner_layout.addView(back_button, 50, 50)
layout.addView(webview, self.width, self.height-60)
mActivity.addContentView(layout, LayoutParams(-1, -1))
webview.setOnKeyListener(KeyListener(self._back_pressed))
self.webview = webview
self.layout = layout
if self.enable_downloads:
webview.setDownloadListener(DownloadListener())
if self.enable_raw_html:
try:
webview.loadDataWithBaseURL("", self.html, "text/html", "utf-8", "")
except Exception as e:
print('Webview.on_open(): ' + str(e))
self.dismiss()
else:
try:
headers_map = HashMap()
for key, value in self.headers.items():
headers_map.put(String(key), String(value))
self.webview.loadUrl(self.url, headers_map)
except Exception as e:
print('Webview.on_open(): ' + str(e))
@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
`