Skip to content
This repository was archived by the owner on Nov 14, 2023. It is now read-only.

Commit 5815b0e

Browse files
committed
copy
0 parents  commit 5815b0e

File tree

4 files changed

+263
-0
lines changed

4 files changed

+263
-0
lines changed

BUILDOZER_README.txt

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# Requires internet permission
2+
3+
android.permissions = INTERNET

README.md

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
Webview
2+
=======
3+
4+
*An embedded Kivy Android web page viewer*
5+
6+
Provides full screen display of "https://" and "file://" urls. To close the viewer use the back gesture or the back button.
7+
8+
The buildozer options are documented in [BUILDOZER_README.txt](https://github.com/RobertFlatt/Android-for-Python/blob/main/webview/BUILDOZER_README.txt)
9+
10+
[Download using the Code button here](https://github.com/RobertFlatt/Android-for-Python)
11+

main.py

+84
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
from kivy.app import App
2+
from kivy.core.window import Window
3+
from kivy.uix.boxlayout import BoxLayout
4+
from kivy.uix.button import Button
5+
from kivy.uix.label import Label
6+
from webview import WebView
7+
from os import listdir
8+
from textwrap import fill
9+
10+
class BrowserApp(App):
11+
def build(self):
12+
self._create_local_file()
13+
self.browser = None
14+
b1 = Button(text='Tap for Google.\nBack button/gesture to return.',
15+
on_press=self.view_google)
16+
b2 = Button(text='Tap for local file.\nBack button/gesture to return.',
17+
on_press=self.view_local_file)
18+
b3 = Button(text='List downloads',
19+
on_press=self.view_downloads)
20+
self.label = Label(text='')
21+
box = BoxLayout(orientation='vertical')
22+
box.add_widget(b1)
23+
box.add_widget(b2)
24+
box.add_widget(b3)
25+
box.add_widget(self.label)
26+
return box
27+
28+
def view_google(self,b):
29+
self.browser = WebView('https://www.google.com',
30+
enable_javascript = True,
31+
enable_downloads = True,
32+
enable_zoom = True)
33+
34+
def view_local_file(self,b):
35+
self.browser = WebView('file://'+self.filename)
36+
37+
def view_downloads(self,b):
38+
if self.browser:
39+
d = self.browser.downloads_directory()
40+
self.label.text = fill(d,40) + '\n'
41+
l = listdir(d)
42+
if l:
43+
for f in l:
44+
self.label.text += f + '\n'
45+
else:
46+
self.label.text = 'No files downloaded'
47+
else:
48+
self.label.text = 'Open a browser first'
49+
50+
def on_pause(self):
51+
if self.browser:
52+
self.browser.pause()
53+
return True
54+
55+
def on_resume(self):
56+
if self.browser:
57+
self.browser.resume()
58+
pass
59+
60+
def _create_local_file(self):
61+
# Create a file for testing
62+
from android.storage import app_storage_path
63+
from jnius import autoclass
64+
from os.path import join, exists
65+
from os import mkdir
66+
67+
Environment = autoclass('android.os.Environment')
68+
path = join(app_storage_path(), Environment.DIRECTORY_DOCUMENTS)
69+
if not exists(path):
70+
mkdir(path)
71+
self.filename = join(path,'from_space.html')
72+
with open(self.filename, "w") as f:
73+
f.write("<html>\n")
74+
f.write(" <head>\n")
75+
f.write(" </head>\n")
76+
f.write(" <body>\n")
77+
f.write(" <h1>Greetings Earthlings<h1>\n")
78+
f.write(" </body>\n")
79+
f.write("</html>\n")
80+
81+
BrowserApp().run()
82+
83+
84+

webview.py

+165
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
# Android **only** HTML viewer, always full screen.
2+
#
3+
# Back button or gesture has the usual browser behavior, except for the final
4+
# back event which returns the UI to the view before the browser was opened.
5+
#
6+
# Base Class: https://kivy.org/doc/stable/api-kivy.uix.modalview.html
7+
#
8+
# Requires: android.permissions = INTERNET
9+
# Uses: orientation = landscape, portrait, or all
10+
# Arguments:
11+
# url : required string, https:// file:// (content:// ?)
12+
# enable_javascript : optional boolean, defaults False
13+
# enable_downloads : optional boolean, defaults False
14+
# enable_zoom : optional boolean, defaults False
15+
#
16+
# Downloads are delivered to app storage see downloads_directory() below.
17+
#
18+
# Tested on api=27 and api=30
19+
#
20+
# Note:
21+
# For api>27 http:// gives net::ERR_CLEARTEXT_NOT_PERMITTED
22+
# This is Android implemented behavior.
23+
#
24+
# Source https://github.com/RobertFlatt/Android-for-Python/webview
25+
26+
from kivy.uix.modalview import ModalView
27+
from kivy.clock import Clock
28+
from android.runnable import run_on_ui_thread
29+
from jnius import autoclass, cast, PythonJavaClass, java_method
30+
31+
WebViewA = autoclass('android.webkit.WebView')
32+
WebViewClient = autoclass('android.webkit.WebViewClient')
33+
LayoutParams = autoclass('android.view.ViewGroup$LayoutParams')
34+
LinearLayout = autoclass('android.widget.LinearLayout')
35+
KeyEvent = autoclass('android.view.KeyEvent')
36+
ViewGroup = autoclass('android.view.ViewGroup')
37+
DownloadManager = autoclass('android.app.DownloadManager')
38+
DownloadManagerRequest = autoclass('android.app.DownloadManager$Request')
39+
Uri = autoclass('android.net.Uri')
40+
Environment = autoclass('android.os.Environment')
41+
Context = autoclass('android.content.Context')
42+
PythonActivity = autoclass('org.kivy.android.PythonActivity')
43+
44+
45+
class DownloadListener(PythonJavaClass):
46+
#https://stackoverflow.com/questions/10069050/download-file-inside-webview
47+
__javacontext__ = 'app'
48+
__javainterfaces__ = ['android/webkit/DownloadListener']
49+
50+
@java_method('(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;J)V')
51+
def onDownloadStart(self, url, userAgent, contentDisposition, mimetype,
52+
contentLength):
53+
mActivity = PythonActivity.mActivity
54+
context = mActivity.getApplicationContext()
55+
visibility = DownloadManagerRequest.VISIBILITY_VISIBLE_NOTIFY_COMPLETED
56+
dir_type = Environment.DIRECTORY_DOWNLOADS
57+
uri = Uri.parse(url)
58+
filepath = uri.getLastPathSegment()
59+
request = DownloadManagerRequest(uri)
60+
request.setNotificationVisibility(visibility)
61+
request.setDestinationInExternalFilesDir(context,dir_type, filepath)
62+
dm = cast(DownloadManager,
63+
mActivity.getSystemService(Context.DOWNLOAD_SERVICE))
64+
dm.enqueue(request)
65+
66+
67+
class KeyListener(PythonJavaClass):
68+
__javacontext__ = 'app'
69+
__javainterfaces__ = ['android/view/View$OnKeyListener']
70+
71+
def __init__(self, listener):
72+
super().__init__()
73+
self.listener = listener
74+
75+
@java_method('(Landroid/view/View;ILandroid/view/KeyEvent;)Z')
76+
def onKey(self, v, key_code, event):
77+
if event.getAction() == KeyEvent.ACTION_DOWN and\
78+
key_code == KeyEvent.KEYCODE_BACK:
79+
return self.listener()
80+
81+
82+
class WebView(ModalView):
83+
# https://developer.android.com/reference/android/webkit/WebView
84+
85+
def __init__(self, url, enable_javascript = False, enable_downloads = False,
86+
enable_zoom = False, **kwargs):
87+
super().__init__(**kwargs)
88+
self.url = url
89+
self.enable_javascript = enable_javascript
90+
self.enable_downloads = enable_downloads
91+
self.enable_zoom = enable_zoom
92+
self.webview = None
93+
self.enable_dismiss = True
94+
self.open()
95+
96+
@run_on_ui_thread
97+
def on_open(self):
98+
mActivity = PythonActivity.mActivity
99+
webview = WebViewA(mActivity)
100+
webview.setWebViewClient(WebViewClient())
101+
webview.getSettings().setJavaScriptEnabled(self.enable_javascript)
102+
webview.getSettings().setBuiltInZoomControls(self.enable_zoom)
103+
webview.getSettings().setDisplayZoomControls(False)
104+
webview.getSettings().setAllowFileAccess(True) #default False api>29
105+
layout = LinearLayout(mActivity)
106+
layout.setOrientation(LinearLayout.VERTICAL)
107+
layout.addView(webview, self.width, self.height)
108+
mActivity.addContentView(layout, LayoutParams(-1,-1))
109+
webview.setOnKeyListener(KeyListener(self._back_pressed))
110+
if self.enable_downloads:
111+
webview.setDownloadListener(DownloadListener())
112+
self.webview = webview
113+
self.layout = layout
114+
try:
115+
webview.loadUrl(self.url)
116+
except Exception as e:
117+
print('Webview.on_open(): ' + str(e))
118+
self.dismiss()
119+
120+
@run_on_ui_thread
121+
def on_dismiss(self):
122+
if self.enable_dismiss:
123+
self.enable_dismiss = False
124+
parent = cast(ViewGroup, self.layout.getParent())
125+
if parent is not None: parent.removeView(self.layout)
126+
self.webview.clearHistory()
127+
self.webview.clearCache(True)
128+
self.webview.clearFormData()
129+
self.webview.destroy()
130+
self.layout = None
131+
self.webview = None
132+
133+
@run_on_ui_thread
134+
def on_size(self, instance, size):
135+
if self.webview:
136+
params = self.webview.getLayoutParams()
137+
params.width = self.width
138+
params.height = self.height
139+
self.webview.setLayoutParams(params)
140+
141+
def pause(self):
142+
if self.webview:
143+
self.webview.pauseTimers()
144+
self.webview.onPause()
145+
146+
def resume(self):
147+
if self.webview:
148+
self.webview.onResume()
149+
self.webview.resumeTimers()
150+
151+
def downloads_directory(self):
152+
# e.g. Android/data/org.test.myapp/files/Download
153+
dir_type = Environment.DIRECTORY_DOWNLOADS
154+
context = PythonActivity.mActivity.getApplicationContext()
155+
directory = context.getExternalFilesDir(dir_type)
156+
return str(directory.getPath())
157+
158+
def _back_pressed(self):
159+
if self.webview.canGoBack():
160+
self.webview.goBack()
161+
else:
162+
self.dismiss()
163+
return True
164+
165+

0 commit comments

Comments
 (0)