From 8ed80eeebab06dba5839a7f08703494a9917348a Mon Sep 17 00:00:00 2001
From: Daniel Thompson <daniel@redfelineninja.org.uk>
Date: Mon, 6 Apr 2020 22:03:05 +0100
Subject: [PATCH] wasp: launcher: Experimental launcher implementation

It is not really the launcher itself that is immature. Rather that the
framework and UI concepts to move between applications isn't complete
yet.
---
 res/app_icon.png                 | Bin 0 -> 7124 bytes
 res/clock_icon.png               | Bin 0 -> 965 bytes
 res/down_arrow.png               | Bin 0 -> 599 bytes
 res/settings_icon.png            | Bin 0 -> 1082 bytes
 res/torch_icon.png               | Bin 0 -> 820 bytes
 res/up_arrow.png                 | Bin 0 -> 593 bytes
 wasp/apps/clock.py               |   3 ++
 wasp/apps/flashlight.py          |   4 ++
 wasp/apps/launcher.py            |  80 +++++++++++++++++++++++++++++++
 wasp/apps/testapp.py             |   4 ++
 wasp/boards/pinetime/manifest.py |   3 +-
 wasp/icons.py                    |   9 ++++
 wasp/wasp.py                     |  34 ++++++++++---
 13 files changed, 129 insertions(+), 8 deletions(-)
 create mode 100644 res/app_icon.png
 create mode 100644 res/clock_icon.png
 create mode 100644 res/down_arrow.png
 create mode 100644 res/settings_icon.png
 create mode 100644 res/torch_icon.png
 create mode 100644 res/up_arrow.png
 create mode 100644 wasp/apps/launcher.py

diff --git a/res/app_icon.png b/res/app_icon.png
new file mode 100644
index 0000000000000000000000000000000000000000..574f75fe221e251189f06146a45601aa10da4f12
GIT binary patch
literal 7124
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
Bic0_h

literal 0
HcmV?d00001

diff --git a/res/clock_icon.png b/res/clock_icon.png
new file mode 100644
index 0000000000000000000000000000000000000000..165bf5166a0616fe2ba319a058a8f3b3e58c9eae
GIT binary patch
literal 965
zcmeAS@N?(olHy`uVBq!ia0vp^2|(<?!3HFsniTOdFfg`cIy(n=Iy);A6y>L7=A<$(
zXiTh~Xzg*>LFU+gyCtq#d~X#VO;~6Z`JvS!OSCIM@e0>ks~IuBn$&nbySf)Yh${H7
zF`~4f@Y>abp#m)O3VwZlU3x)}{!eo1mUwUR?s)nC_mAJzH|&p0T_q{Q9CFskXyuX#
zr`K~W3}?w~6Fj=(h)=Db$mI_*@!vmu{Hu3x^PboLWNN(*rZo5~%uC&sD3~wcpC4uQ
zy!MfVwl~*D%a1D@#Z~H_?0WM_`uU{8b@L>RH_fbkB(X8zRnpGx$eRJ3%R3%-tqR;{
z|9sP-%I?ikYd3$h{mHG&nwvWF^_I2?hm@u%I5qTGX{|Wq)Ku0hA}nd;U-g4scE5+-
z%otIFBAo?nqLUu8|8Ks(*R*@Ga9)rK=a)S<z8yCZxWyX9d;6c=wzt<f1uEFqZRy*8
zjU~;_GrwfZ55Wujd1GSAO)B|!++k#!S8{TKjHbiscpawq>0f*+6{@(utlL?eJ^%3+
z#uGv7FPR@WsAv$h=uK&VdHe3)-|~HT*V|h!ESh+f`LH!GTG*1j-Cck*5d3uBT@R!<
z3p^r=85s1GL71^(seKtxkiEpy*OmPV3k!#oI2+#^cA$`CiEBiObAE1aYF-J0b5Uwy
zNotBhd1gt5g1e`0KzJjcI54{Bdb&7<RNQ(y>tf##1rFD%KmPAOv(GFxdZW^%mpf0|
z_%=Nd6v_3|^mMu%GJ^?dC?f*{2Qb9|SwNlu149Fl#lpa_VC$;#`mQbVr;E-VW{$kI
zDeUxi!Tsx!F5ft{@}r7na>12regabW-~G9CtSo)?8@>;j266i$f2k_{e-o-}%IzMd
z{U9UUW!uyxk9UY^M{{qCY^riInOq_lGW(PqgUP<$ANIy+ALC*#nmt;*ac%9cP2%q)
ztajzg)N5z|T4np{a;-zlnI(e1oz(Am$G=z3SaK<9&!yj|8Z{={Z|aYjw=1h9bV6~R
z$ez&NxSQQy9z{hpDc$+}=1XMp@sPbtv-W<dy6trFSoD3FC$r7&T<E=_Kex%v*fvJ~
z!A#9-#~)wITrPh8xzxtx4et3iTOVKP(9AB}_w7sevHnfjP0_vm;Xf_!|DFGL#!fH!
z-i1+DkKJ6p`|k23>N9UJFYh!zBDW#=)7Ip5-_9--?EdBI(xMjlSEch>$9G_;;R;Fx
cnAHB2_Hzm!W#^vxITNJZ)78&qol`;+0I+_a<^TWy

literal 0
HcmV?d00001

diff --git a/res/down_arrow.png b/res/down_arrow.png
new file mode 100644
index 0000000000000000000000000000000000000000..ef94eb3c0b4737ba4ddfc443f14515720edd8d50
GIT binary patch
literal 599
zcmV-d0;v6oP)<h;3K|Lk000e1NJLTq000mG000RH0ssI2v`9N;0004mX+uL$Nkc;*
zaB^>EX>4Tx04R}tkv&MmKp2MKrj?3B9qb_DkfAzR5EXIMDionYs1;guFuC*(nlvOW
zE{=k0!NH%!s)LKOt`4q(Aov5~<mja6A|-y86k5c1$8itueecWNcYx3+Gu;l21G-VQ
zQi-^f$*xM_SA=Ma=!2**+nkf-Bz(u$Jpz2ci}5V~x<5y+ma`cUkcelQX;q0gh^IHJ
zCg*+P2rDWY@j3CBMHeJ~<httd8|Q+{0?&+C>C`-Ngjgzcu-d__Xz9dL#8FK*C|}5U
zta0Arte0!7bx;06F=wnSbDh>O5?I6%WJpj^LkVS65u@E8#YT$G<39c&*DsSxC07ZI
z91EyGhvNFd|KN9T?fm3~mn6o4t{2Do7y-h&K(p>R-^Y&AJOP5wz?ISVR~o>~C+YRJ
z7Ci#`w}Ff6wx;d@mpj1Vlc`vWD+Or@`8@D`M&FbL25y0#HLq{2bDTZ^Y3fz-1~@nb
zMhld^?(^>M&c6Ly)9T+3-3oGRsRnkS00009a7bBm000XU000XU0RWnu7ytkO2XskI
zMF->r1Pm29T1i#g0000PbVXQnLvL+uWo~o;Lvm$dbY)~9cWHEJAV*0}P*;Ht7XSbN
zK}keGR2b8hjzJ0lFbKn%{r}J1L69MHxSmuJ72EpB(r1t~KLQZd#0i+}-%hUPoOVvq
lRk%7QeG!UN=r*>y_cmQS8v%C!aSZ?f002ovPDHLkV1iRk0qg((

literal 0
HcmV?d00001

diff --git a/res/settings_icon.png b/res/settings_icon.png
new file mode 100644
index 0000000000000000000000000000000000000000..d1141b03052397335cfa703f4dbf7826c2dbc88b
GIT binary patch
literal 1082
zcmV-A1jYM_P)<h;3K|Lk000e1NJLTq003YB002M;1^@s6<uq~*0004mX+uL$Nkc;*
zaB^>EX>4Tx04R}tkv&MmKp2MKrj?3B9qb_DkfAzR5EXIMDionYs1;guFuC*(nlvOW
zE{=k0!NH%!s)LKOt`4q(Aov5~<mja6A|-y86k5c1$8itueecWNcYx3+Gu;l21G-VQ
zQi-^f$*xM_SA=Ma=!2**+nkf-Bz(u$Jpz2ci}5V~x<5y+ma`cUkcelQX;q0gh^IHJ
zCg*+P2rDWY@j3CBMHeJ~<httd8|Q+{0?&+C>C`-Ngjgzcu-d__Xz9dL#8FK*C|}5U
zta0Arte0!7bx;06F=wnSbDh>O5?I6%WJpj^LkVS65u@E8#YT$G<39c&*DsSxC07ZI
z91EyGhvNFd|KN9T?fm3~mn6o4t{2Do7y-h&K(p>R-^Y&AJOP5wz?ISVR~o>~C+YRJ
z7Ci#`w}Ff6wx;d@mpj1Vlc`vWD+Or@`8@D`M&FbL25y0#HLq{2bDTZ^Y3fz-1~@nb
zMhld^?(^>M&c6Ly)9T+3-3oGRsRnkS00009a7bBm000XU000XU0RWnu7ytkO2XskI
zMF->r1q&__?!9CA0000PbVXQnLvL+uWo~o;Lvm$dbY)~9cWHEJAV*0}P*;Ht7XSbP
zBuPX;RCwC$T*0oxAPgg@|NqOp!&4=MN*fp@PU@UCjk4i@9aC0GDV1aprI>teH$=qb
zdvju#1{3hb1fbE6<!=%z4PgAo@#R^MAj+?=0B^wWPDCd7wqwWy-*zlVgk|h`643|H
zh_MU}Lk%9OE=K|1tMHS8NI`S6zonb=vr>vHDmqXBUdici^&5(UuK2s2)g$~~x4-L`
z378aC?F{xH*clCc5xE7p_nGFpo^DVBF2FrskBVyD_Uc*@AqCAQW#_e@Ou&wriDuRg
zgc&{q%8JqwVTC|wK%Im;&ev8on(I;M39byC0bkp;6=+h>V!El@dD)W4I{?!-))ibo
zTIY{CY!?a845&Borx~l6(KCHmCL&wY$r}~REcI9<!BhjDfLZ}a&kdt^1KyBf8Oj7O
zzUl*>4A_dD%U;zJb_L9qlB0gWy3&HCAj>4o^zn2vC!_|PgC0%R$ZbYV-`3j|F7|?2
zEzqn4FGo`{3cyd`u5iML+tCm!Knu)WqO|)&Xa~3sB=C1vEC@+}GGaTxK+^_zC;W?P
z@^D%j?ri84&yR>_3;OxKK4-)P{7_=Hfc|R0NI@x09YwV4H>3viHsfX-Yu)~?UsBK}
zV31w0tAEI}M#}RRgqt!ndbmq~2Vo6ML7sso1u+2(GYX3I=o&XGAQDf%IhO48her|o
zZBI?)L;H*qzyR_AsH-8&!7Pv3z=wCU3}slvAM_nbypjZu>i_@%07*qoM6N<$f?@IL
AssI20

literal 0
HcmV?d00001

diff --git a/res/torch_icon.png b/res/torch_icon.png
new file mode 100644
index 0000000000000000000000000000000000000000..bbd4db3bb8ecc7e891ef7187deeb57f27239a10f
GIT binary patch
literal 820
zcmV-41Izr0P)<h;3K|Lk000e1NJLTq003YB002M;0ssI2YF6c00004mX+uL$Nkc;*
zaB^>EX>4Tx04R}tkv&MmKp2MKrj?3B9qb_DkfAzR5EXIMDionYs1;guFuC*(nlvOW
zE{=k0!NH%!s)LKOt`4q(Aov5~<mja6A|-y86k5c1$8itueecWNcYx3+Gu;l21G-VQ
zQi-^f$*xM_SA=Ma=!2**+nkf-Bz(u$Jpz2ci}5V~x<5y+ma`cUkcelQX;q0gh^IHJ
zCg*+P2rDWY@j3CBMHeJ~<httd8|Q+{0?&+C>C`-Ngjgzcu-d__Xz9dL#8FK*C|}5U
zta0Arte0!7bx;06F=wnSbDh>O5?I6%WJpj^LkVS65u@E8#YT$G<39c&*DsSxC07ZI
z91EyGhvNFd|KN9T?fm3~mn6o4t{2Do7y-h&K(p>R-^Y&AJOP5wz?ISVR~o>~C+YRJ
z7Ci#`w}Ff6wx;d@mpj1Vlc`vWD+Or@`8@D`M&FbL25y0#HLq{2bDTZ^Y3fz-1~@nb
zMhld^?(^>M&c6Ly)9T+3-3oGRsRnkS00009a7bBm000XU000XU0RWnu7ytkO2XskI
zMF->r1qcTUBG`+k0000PbVXQnLvL+uWo~o;Lvm$dbY)~9cWHEJAV*0}P*;Ht7XSbO
z9!W$&RCwC$nMn@BAPfW{%KyL297c*p44VLhajFivWHU`;vp5F;00000000000F&_N
zh8{wQ8{<6!C3F$h=sr}Me<8}k*>}*>{-j5~cO{0{G3rze(&C_&@8}w0QMla}C^x(8
zKD|RM*{WP}skSYI(EaXA2MOiE!S6?JjaX*LLDK4s6z`;i_LTX>#iB9$3jO^uvLF!a
zvujol&%J8PjQ#Tw33YNAcZ)_nNqT(_YN_$Rfh}IIRc4~nLu{{uj0~}kHN>_x(XOeZ
zP+&&Nyj-{4s-hX02Wi<4(z)8mRk2s>5H%*g<TK+*)1RVI*$(9g>>Fz1jv{-~Rs?cw
yryA2W;#|y}Kn11Vd+Y-M0000000000I`{ymQ-~0^R{FL80000<MNUMnLSTZba%{^0

literal 0
HcmV?d00001

diff --git a/res/up_arrow.png b/res/up_arrow.png
new file mode 100644
index 0000000000000000000000000000000000000000..c990949906db5692d795c35e8a0dc9bd55c5dcc8
GIT binary patch
literal 593
zcmV-X0<QguP)<h;3K|Lk000e1NJLTq000mG000RH0ssI2v`9N;0004mX+uL$Nkc;*
zaB^>EX>4Tx04R}tkv&MmKp2MKrj?3B9qb_DkfAzR5EXIMDionYs1;guFuC*(nlvOW
zE{=k0!NH%!s)LKOt`4q(Aov5~<mja6A|-y86k5c1$8itueecWNcYx3+Gu;l21G-VQ
zQi-^f$*xM_SA=Ma=!2**+nkf-Bz(u$Jpz2ci}5V~x<5y+ma`cUkcelQX;q0gh^IHJ
zCg*+P2rDWY@j3CBMHeJ~<httd8|Q+{0?&+C>C`-Ngjgzcu-d__Xz9dL#8FK*C|}5U
zta0Arte0!7bx;06F=wnSbDh>O5?I6%WJpj^LkVS65u@E8#YT$G<39c&*DsSxC07ZI
z91EyGhvNFd|KN9T?fm3~mn6o4t{2Do7y-h&K(p>R-^Y&AJOP5wz?ISVR~o>~C+YRJ
z7Ci#`w}Ff6wx;d@mpj1Vlc`vWD+Or@`8@D`M&FbL25y0#HLq{2bDTZ^Y3fz-1~@nb
zMhld^?(^>M&c6Ly)9T+3-3oGRsRnkS00009a7bBm001r{001r{0eGc9b^rhX2XskI
zMF->r1Pm1mlAZ)x0000PbVXQnLvL+uWo~o;Lvm$dbY)~9cWHEJAV*0}P*;Ht7XSbN
zJ4r-AR2b8hj6n_nAPB>#|Nrwmn5Y3F*z4MvWk`~gPNepkp$Ou!Yk1qSp|o8?$G#hy
f_HO9gi(#Kk@~ImE3U6Cw00000NkvXXu0mjf^Z^4O

literal 0
HcmV?d00001

diff --git a/wasp/apps/clock.py b/wasp/apps/clock.py
index 4feba63..4236d9a 100644
--- a/wasp/apps/clock.py
+++ b/wasp/apps/clock.py
@@ -3,6 +3,7 @@
 
 import wasp
 
+import icons
 import fonts.clock as digits
 
 DIGITS = (
@@ -25,6 +26,8 @@ class ClockApp():
 
     Shows a time (as HH:MM) together with a battery meter and the date.
     """
+    NAME = 'Clock'
+    ICON = icons.clock
 
     def __init__(self):
         self.meter = wasp.widgets.BatteryMeter()
diff --git a/wasp/apps/flashlight.py b/wasp/apps/flashlight.py
index 13e3443..c4702a0 100644
--- a/wasp/apps/flashlight.py
+++ b/wasp/apps/flashlight.py
@@ -3,11 +3,15 @@
 
 import wasp
 
+import icons
+
 class FlashlightApp(object):
     """Trivial flashlight application.
 
     Shows a pure white screen with the backlight set to maximum.
     """
+    NAME = 'Torch'
+    ICON = icons.torch
 
     def foreground(self):
         """Activate the application."""
diff --git a/wasp/apps/launcher.py b/wasp/apps/launcher.py
new file mode 100644
index 0000000..274ea9c
--- /dev/null
+++ b/wasp/apps/launcher.py
@@ -0,0 +1,80 @@
+# SPDX-License-Identifier: LGPL-3.0-or-later
+# Copyright (C) 2020 Daniel Thompson
+
+import wasp
+import icons
+
+class LauncherApp():
+    """An application launcher application.
+    """
+    NAME = 'Launcher'
+    ICON = icons.app
+
+    def foreground(self):
+        """Activate the application."""
+        self._page = 0
+        self._draw()
+        wasp.system.request_event(wasp.EventMask.TOUCH |
+                                  wasp.EventMask.SWIPE_UPDOWN)
+
+    def swipe(self, event):
+        i = self._page
+        n = self._num_pages
+        if event[0] == wasp.EventType.UP:
+            i += 1
+            if i >= n:
+                i -= 1
+                wasp.watch.vibrator.pulse()
+                return
+        else:
+            i -= 1
+            if i < 0:
+                wasp.system.switch(wasp.system.applications[0])
+                return
+
+        self._page = i
+        wasp.watch.display.mute(True)
+        self._draw()
+        wasp.watch.display.mute(False)
+
+    def touch(self, event):
+        page = self._get_page(self._page)
+        x = event[1]
+        y = event[2]
+        app = page[2 * (y // 120) + (x // 120)]
+        if app:
+            wasp.system.switch(app)
+        else:
+            wasp.watch.vibrator.pulse()
+
+    @property
+    def _num_pages(self):
+        """Work out what the highest possible pages it."""
+        num_apps = len(wasp.system.applications)
+        return (num_apps + 3) // 4
+
+    def _get_page(self, i):
+        apps = wasp.system.applications
+        page = apps[4*i: 4*(i+1)]
+        while len(page) < 4:
+            page.append(None)
+        return page
+
+    def _draw(self):
+        """Redraw the display from scratch."""
+        def draw_app(app, x, y):
+            if not app:
+                return
+            draw.set_color(0xffff)
+            draw.rleblit(app.ICON, (x+13, y+12))
+            draw.set_color(0xbdb6)
+            draw.string(app.NAME, x, y+120-30, 120)
+
+        draw = wasp.watch.drawable
+        page = self._get_page(self._page)
+        
+        draw.fill()
+        draw_app(page[0],   0,   0)
+        draw_app(page[1], 120,   0)
+        draw_app(page[2],   0, 120)
+        draw_app(page[3], 120, 120)
diff --git a/wasp/apps/testapp.py b/wasp/apps/testapp.py
index 8c15848..478249f 100644
--- a/wasp/apps/testapp.py
+++ b/wasp/apps/testapp.py
@@ -3,10 +3,13 @@
 
 import machine
 import wasp
+import icons
 
 class TestApp():
     """Simple test application.
     """
+    NAME = 'Self Test'
+    ICON = icons.app
 
     def __init__(self):
         self.tests = ('Touch', 'String', 'Button', 'Crash')
@@ -57,6 +60,7 @@ class TestApp():
     def benchmark_string(self):
         draw = wasp.watch.drawable
         draw.fill(0, 0, 30, 240, 240-30)
+        self.scroll.draw()
         t = machine.Timer(id=1, period=8000000)
         t.start()
         draw.string("The quick brown", 12, 24+24)
diff --git a/wasp/boards/pinetime/manifest.py b/wasp/boards/pinetime/manifest.py
index 191ee3d..30d54e2 100644
--- a/wasp/boards/pinetime/manifest.py
+++ b/wasp/boards/pinetime/manifest.py
@@ -5,8 +5,9 @@ freeze('.', 'watch.py', opt=3)
 freeze('../..',
     (
         'apps/clock.py',
-        'apps/testapp.py',
         'apps/flashlight.py',
+        'apps/launcher.py',
+        'apps/testapp.py',
         'boot.py',
         'demo.py',
         'draw565.py',
diff --git a/wasp/icons.py b/wasp/icons.py
index c3089c4..76b5ee7 100644
--- a/wasp/icons.py
+++ b/wasp/icons.py
@@ -4,6 +4,15 @@
 # 1-bit RLE, generated from res/battery.png, 189 bytes
 battery = (36, 48, b'\x97\x0e\x14\x12\x11\x14\x10\x14\x0c\x08\x0c\x08\x08\x08\x0c\x08\x08\x08\x0c\x08\x08\x08\x0c\x08\x08\x04\x14\x04\x08\x04\x14\x04\x08\x04\x0c\x04\x04\x04\x08\x04\x0b\x05\x04\x04\x08\x04\n\x06\x04\x04\x08\x04\t\x07\x04\x04\x08\x04\x08\x07\x05\x04\x08\x04\x07\x07\x06\x04\x08\x04\x06\x07\x07\x04\x08\x04\x05\x07\x08\x04\x08\x04\x04\x0e\x02\x04\x08\x04\x03\x0f\x02\x04\x08\x04\x02\x10\x02\x04\x08\x04\x02\x10\x02\x04\x08\x04\x02\x0f\x03\x04\x08\x04\x02\x0e\x04\x04\x08\x04\x08\x07\x05\x04\x08\x04\x07\x07\x06\x04\x08\x04\x06\x07\x07\x04\x08\x04\x05\x07\x08\x04\x08\x04\x04\x07\t\x04\x08\x04\x04\x06\n\x04\x08\x04\x04\x05\x0b\x04\x08\x04\x04\x04\x0c\x04\x08\x04\x14\x04\x08\x04\x14\x04\x08\x04\x14\x04\x08\x04\x14\x04\x08\x1c\x08\x1c\x08\x1c\x08\x1c\x98')
 
+# 1-bit RLE, generated from res/app_icon.png, 441 bytes
+app = (96, 64, b'\x1e$<$<$;&\x97,20/2-4,\x03.\x03,\x03.\x03,\x03.\x03,\x03.\x03,\x03.\x03,\x03\x0c\x03\x10\x03\x0c\x03,\x03\n\x07\x0c\x07\n\x03,\x03\t\x03\x02\x04\n\x04\x02\x03\t\x03,\x03\x08\x02\x07\x02\x08\x02\x07\x02\x08\x03,\x03\x07\x02\t\x02\x06\x02\t\x02\x07\x03,\x03\x06\x02\x0b\x02\x04\x02\x0b\x02\x06\x03,\x03\x06\x02\x0b\x02\x04\x02\x0b\x02\x06\x03,\x03\x05\x02\x0c\x02\x04\x02\x0c\x02\x05\x03,\x03\x05\x02\x0c\x02\x04\x02\x0c\x02\x05\x03,\x03\x05\x03\x0b\x02\x04\x02\x0b\x03\x05\x03,\x03\x06\x02\x0b\x02\x04\x02\x0b\x02\x06\x03,\x03\x06\x02\x0b\x02\x04\x02\x0b\x01\x07\x03,\x03\x07\x02\n\x02\x04\x02\n\x02\x07\x03+\x04\x08\x02\t\x02\x04\x02\t\x02\x08\x03*\x05\t\x0c\x04\x0c\t\x03*\x05\n\x0b\x04\x0b\n\x03*\x05.\x03*\x05.\x03*\x05.\x03*\x05.\x03*\x05\n\x0b\x04\x0b\n\x03+\x04\t\x0c\x04\x0c\t\x03,\x03\x08\x02\t\x02\x04\x02\t\x02\x08\x03,\x03\x07\x02\n\x02\x04\x02\n\x02\x07\x03,\x03\x06\x02\x0b\x02\x04\x02\x0b\x01\x07\x03,\x03\x06\x02\x0b\x02\x04\x02\x0b\x02\x06\x03,\x03\x05\x03\x0b\x02\x04\x02\x0b\x03\x05\x03,\x03\x05\x02\x0c\x02\x04\x02\x0c\x02\x05\x03,\x03\x05\x02\x0c\x02\x04\x02\x0c\x02\x05\x03,\x03\x06\x02\x0b\x02\x04\x02\x0b\x02\x06\x03,\x03\x06\x02\x0b\x02\x04\x02\x0b\x02\x06\x03,\x03\x07\x02\t\x02\x06\x02\t\x02\x07\x03,\x03\x08\x02\x07\x02\x08\x02\x07\x02\x08\x03,\x03\t\x03\x02\x04\n\x04\x02\x03\t\x03,\x03\n\x06\x0e\x06\n\x03,\x03\x0c\x03\x10\x03\x0c\x03,\x03.\x03,\x03.\x03,\x03.\x03,\x03.\x03,\x03.\x03,4-2/02,\x97&;$<$<$\x1e')
+
+# 1-bit RLE, generated from res/clock_icon.png, 301 bytes
+clock = (96, 64, b'\xff\x00\xff\x00\xff\x00\xff\x00\xff\x00\xff\x00\xcd\x06\r\x06!\x05\x0b\x08\x0c\x08\x0b\n\x1d\t\x07\x0c\n\x08\n\x0c\x1b\x0b\x06\x0e\x08\x03\x02\x03\n\x04\x05\x04\x1a\x04\x03\x04\x06\x02\x08\x04\r\x03\t\x04\x07\x03\x19\x04\x05\x04\x10\x04\x0c\x03\t\x03\t\x02\x19\x03\x07\x03\x11\x03\x0c\x03\t\x03\t\x03\n\x04\t\x04\x07\x04\x10\x03\x0c\x03\t\x03\t\x03\n\x04\t\x03\t\x03\x10\x03\x0c\x03\t\x03\t\x03\n\x04\t\x03\t\x03\x10\x03\x0c\x03\t\x04\x07\x04\n\x04\t\x03\t\x03\x0f\x04\x0c\x03\n\x04\x05\x05\n\x04\t\x03\x03\x02\x04\x03\x0e\x04\r\x03\n\n\x01\x03\x17\x03\x02\x04\x03\x03\r\x05\r\x03\x0b\t\x01\x03\x17\x03\x02\x03\x04\x03\x0c\x05\x0e\x03\r\x05\x03\x03\x17\x03\t\x03\x0b\x05\x0f\x03\x15\x03\x17\x03\t\x03\n\x05\x10\x03\x14\x04\x17\x03\t\x03\t\x05\x11\x03\x14\x03\x18\x04\x07\x04\x08\x04\x13\x03\x14\x03\x19\x03\x07\x03\x08\x04\x14\x03\x13\x04\x0b\x04\n\x03\x06\x04\x07\x04\x15\x03\x0b\x01\x05\x05\x0c\x04\x0b\x04\x03\x04\x07\x03\x12\r\x06\n\r\x04\x0b\x0b\x06\x0f\x07\r\x06\t\x0e\x04\x0c\t\x07\x0f\x07\r\x07\x06\x10\x04\x0e\x05\t\x0f\xff\x00\xff\x00\xff\x00\xff\x00\xff\x00\xff\x00\xff\x00\xff\x00\xaa')
+
+# 1-bit RLE, generated from res/torch_icon.png, 283 bytes
+torch = (96, 64, b'\xff\x00\xff\x00\xff\x00\xff\x00\xff\x00\xff\x00\xff\x00e\x06W\nT\x04\x06\x02S\x03\x07\x02S\x02\n\x01\x0b\x029\x05\x08\x02\t\x02\x08\x03:\x07\x06\x02\x0b\x01\x06\x02$(\n\x02\x03\x03%(\x0c\x01+\x02%\x01\x0b\x02+\x02%\x01\x0c\x01+\x02\x05\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x05\x01\x0b\x02+\x02\x04\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x04\x01\x0c\x01+\x02%\x01\x0b\x02\x03\n\x1e\x02\x05\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x05\x01\x0c\x01+\x02\x04\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x04\x01\x0b\x02+\x02%\x01\x0c\x01+\x02%\x01\x0b\x02+(\x0c\x01,(\n\x02\x03\x03L\x02\x0b\x01\x06\x02K\x02\t\x02\x08\x03H\x02\n\x01\x0b\x02G\x03\x07\x02U\x04\x06\x02V\nY\x06\xff\x00\xff\x00\xff\x00\xff\x00\xff\x00\xff\x00\xe2')
+
 # 1-bit RLE, generated from res/up_arrow.png, 16 bytes
 up_arrow = (16, 9, b'\x07\x02\r\x04\x0b\x06\t\x08\x07\n\x05\x0c\x03\x0e\x01 ')
 
diff --git a/wasp/wasp.py b/wasp/wasp.py
index a18c466..6016bd5 100644
--- a/wasp/wasp.py
+++ b/wasp/wasp.py
@@ -15,6 +15,7 @@ import widgets
 
 from apps.clock import ClockApp
 from apps.flashlight import FlashlightApp
+from apps.launcher import LauncherApp
 from apps.testapp import TestApp
 
 class EventType():
@@ -86,6 +87,8 @@ class Manager():
         self.applications = []
         self.blank_after = 15
         self.charging = True
+        self.launcher = LauncherApp()
+
         self._brightness = 2
         self._button = PinHandler(watch.button)
 
@@ -142,23 +145,40 @@ class Manager():
         quick application ring. Applications on the ring are not permitted
         to subscribe to :py:data`EventMask.SWIPE_LEFTRIGHT` events.
 
+        Swipe up is used to bring up the launcher. Clock applications are not
+        permitted to subscribe to :py:data`EventMask.SWIPE_UPDOWN` events since
+        they should expect to be the default application (and is important that
+        we can trigger the launcher from the default application).
+
         :param int direction: The direction of the navigation
         """
         app_list = self.applications
 
         if direction == EventType.LEFT:
-            i = app_list.index(self.app) + 1
-            if i >= len(app_list):
+            if self.app in app_list:
+                i = app_list.index(self.app) + 1
+                if i >= len(app_list):
+                    i = 0
+            else:
                 i = 0
             self.switch(app_list[i])
         elif direction == EventType.RIGHT:
-            i = app_list.index(self.app) - 1
-            if i < 0:
-                i = len(app_list)-1
+            if self.app in app_list:
+                i = app_list.index(self.app) - 1
+                if i < 0:
+                    i = len(app_list)-1
+            else:
+                i = 0
             self.switch(app_list[i])
+        elif direction == EventType.UP:
+            self.switch(self.launcher)
+        elif direction == EventType.DOWN:
+            if self.app != app_list[0]:
+                self.switch(app_list[0])
+            else:
+                watch.vibrator.pulse()
         elif direction == EventType.HOME:
-            i = app_list.index(self.app)
-            if i != 0:
+            if self.app != app_list[0]:
                 self.switch(app_list[0])
             else:
                 self.sleep()