Based on @omz script Add to Home Screen,
this script allows the creation of an home screen shortcut to run a script
- to be selected in Pythonista local or iCloud files
- to define any icon web file, not only builtin icons
- to add eventual arguments to the url scheme
The safari page to share to an home screen shortcut shows the icon, its url, its title and the url scheme.
These informations need to be entered in a form_dialog:
- when you put the cursor on the script field, you get a file picker
- when you modify the icon url, the image is directly showed
# from http://www.editorial-workflows.com/workflow/5872060161064960/e831ijSVsok
# converted for python3 which uses bytes ipo strings
import dialogs
import http.server
import webbrowser
import base64
import os
import ui
import dialogs
import requests
from File_Picker import *
page_template = '''<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN">
<html>
<head>
<meta charset="utf-8">
<title>
{{TITLE}}
</title>
<link rel="apple-touch-icon" sizes="76x76" href="{{ICON_URL}}">
<link rel="apple-touch-icon" sizes="120x120" href="{{ICON_URL}}">
<link rel="apple-touch-icon" sizes="152x152" href="{{ICON_URL}}">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black">
<meta name="viewport" content="initial-scale=1 maximum-scale=1 user-scalable=no">
<style type="text/css">
body {
background-color: #023a4e;
-webkit-text-size-adjust: 100%;
-webkit-user-select: none;
}
#help {
display: none;
color: white;
font-family: "Avenir Next", helvetica, sans-serif;
padding: 40px;
}
.help-step {
border-radius: 8px;
background-color: #047ea9;
color: white;
font-size: 20px;
padding: 20px;
margin-bottom: 20px;
}
.icon {
background-image: url({{ICON_URL}});
width: 76px;
height: 76px;
background-size: 76px 76px;
border-radius: 15px;
margin: 0 auto;
}
.share-icon {
width: 32px;
height: 27px;
display: inline-block;
background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAAA2CAQAAADdG1eJAAAAyElEQVRYw+3YvQrCMBSG4a/SKxC8M6dOnQShk9BJKAi9OcFLKrwuaamDRRtMLHxnCBySch7yR6i07aCjy1seyEbgxhhd3vI5CKH8ddamJNAD0EoAEm1SQijfSCNAoklGoAcG6pAFgMQpCYELMFBN+QSQqBmA828BBx4cZ/kMIFFxZ5/2NLwA1sQu92VuQHZAubTB3vcVRfz4/5+BZfnnY5cPqjehAQYYYIABBhhQxn3+zYPFS2CAAQYYYIABBqx6kMT+htzADDwBk2GVUD9m13YAAAAASUVORK5CYII=');
background-size: 32px 27px;
vertical-align: -4px;
}
.icon-title {
font-family: "Helvetica Neue", helvetica, sans-serif;
text-align: center;
font-size: 16px;
margin-top: 10px;
margin-bottom: 30px;
}
@media only screen and (max-width: 767px) {
#help {
padding: 30px 0px 10px 0px;
}
.help-step {
padding: 10px;
}
}
</style>
</head>
<body>
<div id="help">
<div class="icon"></div>
<div class="icon-title">
{{ICON_URL}}
<div class="icon-title">
{{TITLE}}
<div class="icon-title">
{{SHORTCUT_URL}}
</div>
<div class="help-step">
<strong>1.</strong> Tap the
<div class="share-icon"></div>button in the toolbar
</div>
<div class="help-step">
<strong>2.</strong> Select "Add to Home Screen"
</div>
<div class="help-step">
<strong>3.</strong> Tap "Add"
</div>
</div><script type="text/javascript">
if (navigator.standalone) {
window.location = "{{SHORTCUT_URL}}";
} else {
var helpDiv = document.getElementById("help");
helpDiv.style.display = "block";
}
</script>
</body>
</html>
'''
redirect_template = '''<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Loading...</title>
</head>
<body>
<script>
window.location = "data:text/html;base64,{{DATA}}";
</script>
</body>
</html>
'''
def main():
global page_template,redirect_template,redirect_page
fields = []
field = {'title':'title','type':'text','value':''}
fields.append(field)
field = {'title':'icloud','type':'switch','value':False}
fields.append(field)
field = {'title':'script','type':'text','value':''}
fields.append(field)
field = {'title':'arguments','type':'text', 'value':''}
fields.append(field)
icon_url = 'https://i.imgur.com/en0NsLZ.jpg'
field = {'title':'icon url','type':'text','value':icon_url}
fields.append(field)
f = myform_dialog(title='Home Screen Shortcut', done_button_title='ok',fields=fields, sections=None)
#print(f)
if f == None:
# Close/cancel pressed
return
script = f['script']
#print(script)
if 'Mobile Documents/iCloud' in script:
icloud = True
else:
icloud = False
t = 'Pythonista3/Documents/'
i = script.find(t)
script = script[i+len(t):-3]# remove path and .py
arguments = f['arguments']
shortcut_url = 'pythonista3://' + script + '?action=run'
if icloud:
shortcut_url = shortcut_url + '&root=icloud'
if arguments != '':
shortcut_url = shortcut_url + '&' + arguments
#shortcut_url = 'pythonista3://patience.txt'
#shortcut_url = 'pythonista3://Affiches_films?action=run&root=icloud&argv=from_launcher'
title = f['title']
icon_url = f['icon url']
#icon_url = 'https://i.imgur.com/gw1k9Dn.jpg'
#icon_url = 'https://i.imgur.com/en0NsLZ.jpg'
page = page_template.replace('{{TITLE}}', title).replace('{{SHORTCUT_URL}}', shortcut_url).replace('{{ICON_URL}}', icon_url)
page_b64 = str(base64.b64encode(page.encode()),'utf-8')
redirect_page = redirect_template.replace('{{DATA}}', page_b64).encode()
httpd = http.server.HTTPServer(('', 0), MyHandler)
port = str(httpd.socket.getsockname()[1])
webbrowser.open('safari-http://localhost:' + port)
httpd.handle_request()
class MyHandler (http.server.BaseHTTPRequestHandler):
def do_GET(s):
global redirect_page
s.send_response(200)
s.send_header('Content-Type', 'text/html')
s.end_headers()
s.wfile.write(redirect_page)
def log_message(self, format, *args):
pass
class MyTextFieldDelegate (object):
global c
def textfield_should_begin_editing(self, textfield):
if textfield.name == 'script':
icloud = c.values['icloud']
py_file = file_picker_dialog('Select script', multiple=False, select_dirs=False,file_pattern=r'^.*\.py$',from_dialog=[c,textfield], icloud=icloud)
if py_file != None:
textfield.text = py_file
c.values['script'] = py_file
def textfield_did_end_editing(self, textfield):
if textfield.name == 'icon url':
try:
url = textfield.text
r = requests.get(url)
c.container_view['icon'].image = ui.Image.from_data(r.content)
except Exception as e:
return
def myform_dialog(title='', fields=None,sections=None, done_button_title='ok'):
global c
sections = [('', fields)]
c = dialogs._FormDialogController(title, sections, done_button_title=done_button_title)
y = 40
for s in c.cells:
for cell in s:
y = y + cell.height
w = c.container_view.width
h = c.container_view.height - y
x = 0
icon = ui.ImageView(frame=(x,y,w,h))
icon.name = 'icon'
icon.content_mode = ui.CONTENT_SCALE_ASPECT_FIT
c.container_view.add_subview(icon)
url = c.values['icon url']
if url != '':
try:
r = requests.get(url)
icon.image = ui.Image.from_data(r.content)
except Exception as e:
console.hud_alert('download error '+url)
for i in range(0,len(c.cells[0])): # loop on rows of section 0
cell = c.cells[0][i] # ui.TableViewCell of row i
# some fields types are subviews of the cell:
# text,number,url,email,password,switch
# but check, date and time are not set as subviews of cell.content_view
if len(cell.content_view.subviews) > 0:
tf = cell.content_view.subviews[0] # ui.TextField of value in row
# attention: tf.name not set for date fields
if tf.name in ['script','icon url']: # No check for switch
tf.delegate = MyTextFieldDelegate() # delegate to check while typing
break
c.container_view.present('sheet')
c.container_view.wait_modal()
# Get rid of the view to avoid a retain cycle:
c.container_view = None
if c.was_canceled:
return None
return c.values
# Protect against import
if __name__ == '__main__':
main()
The file picker is based on @omz script File Picker.
As it is called by a form dialog, I've had to modify it because the scrolling did not work, perhaps due to two successive wait_modal.
…
…
…
def done_action(self, sender):
self.selected_entries = [self.flat_entries[i[1]] for i in self.table_view.selected_rows if self.flat_entries[i[1]].enabled]
if self.from_dialog != None:
if self.selected_entries != None:
paths = [e.path for e in self.selected_entries]
c = self.from_dialog[0]
tf = self.from_dialog[1]
c.values[tf.name] = paths[0]
tf.text = paths[0]
self.view.close()
def file_picker_dialog(title=None, root_dir=None, multiple=False,
select_dirs=False, file_pattern=None, show_size=True,from_dialog=None,icloud=False):
if root_dir is None:
root_dir = os.path.expanduser('~/')
if title is None:
title = os.path.split(root_dir)[1]
if icloud:
root_node = FileTreeNode('/private/var/mobile/Library/Mobile Documents/iCloud~com~omz-software~Pythonista3/Documents/', show_size, select_dirs, file_pattern) # bug? does not use root_dir
else:
root_node = FileTreeNode(root_dir, show_size, select_dirs, file_pattern) # bug? does not use root_dir
root_node.title = title or ''
picker = TreeDialogController(root_node, allow_multi=multiple)
picker.from_dialog = from_dialog
picker.view.present('sheet')
if from_dialog == None:
picker.view.wait_modal()
else:
# called by a textfield of form_dialog.
# if wait_modal, scroll does not function
# return process performd in done_action, see above
return
if picker.selected_entries is None:
return None
paths = [e.path for e in picker.selected_entries]
if multiple:
return paths
else:
return paths[0]


