From 1a4b678b2b815884cb712244d8a1b5e50a5cbd86 Mon Sep 17 00:00:00 2001 From: Daniel Thompson Date: Sun, 20 Jun 2021 10:28:27 +0100 Subject: [PATCH] apps: FacesApp: Add a watch face chooser This app is enabled by default and allows users to select a watch face based on a fullscreen preview of how the app will draw the screen. Signed-off-by: Daniel Thompson --- docs/apps.rst | 2 + res/FacesApp.png | Bin 0 -> 5994 bytes wasp/apps/faces.py | 69 +++++++++++++++++++++++++++++++ wasp/apps/software.py | 5 +-- wasp/boards/manifest_240x240.py | 1 + wasp/boards/simulator/test_qa.py | 2 +- wasp/wasp.py | 5 ++- 7 files changed, 78 insertions(+), 6 deletions(-) create mode 100644 res/FacesApp.png create mode 100644 wasp/apps/faces.py diff --git a/docs/apps.rst b/docs/apps.rst index 350c5d5..39205ae 100644 --- a/docs/apps.rst +++ b/docs/apps.rst @@ -9,6 +9,8 @@ Application Library Watch faces ----------- +.. automodule:: apps.faces + .. automodule:: apps.clock .. automodule:: apps.chrono diff --git a/res/FacesApp.png b/res/FacesApp.png new file mode 100644 index 0000000000000000000000000000000000000000..229e85d2ce2599794719f71e89ca376a9bb09fff GIT binary patch literal 5994 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 VCZEzF&HxP^a#LMLtq^M&@E@xE{~G`R literal 0 HcmV?d00001 diff --git a/wasp/apps/faces.py b/wasp/apps/faces.py new file mode 100644 index 0000000..7e3ed3e --- /dev/null +++ b/wasp/apps/faces.py @@ -0,0 +1,69 @@ +# SPDX-License-Identifier: LGPL-3.0-or-later +# Copyright (C) 2020 Daniel Thompson +"""Watch Face Chooser +~~~~~~~~~~~~~~~~~~~~~ + +A tool to select a suitable watch face. + +.. figure:: res/FacesApp.png + :width: 179 + +The app is intended to be enabled by default and has, therefore, been carefully +structured to minimize memory usage when the app is not active. +""" + +import wasp +import icons + +class FacesApp(): + """Choose a default watch face.""" + NAME = 'Faces' + ICON = icons.clock + + def foreground(self): + """Activate the application.""" + choices = [] + choices.append(('clock', 'Clock')) + choices.append(('chrono', 'Chrono')) + choices.append(('dual_clock', 'DualClock')) + choices.append(('fibonacci_clock', 'FibonacciClock')) + choices.append(('word_clock', 'WordClock')) + + self.choices = choices + self.choice = 0 + self.si = wasp.widgets.ScrollIndicator() + + self._update() + wasp.system.request_event(wasp.EventMask.SWIPE_UPDOWN) + + def background(self): + del self.choices + del self.choice + del self.si + + # When the watch face redraws then the change to the scrolling indicator + # is a little subtle. Let's provide some haptic feedback too so the user + # knows something has happened. + wasp.watch.vibrator.pulse() + + def swipe(self, event): + """Notify the application of a touchscreen swipe event.""" + choice = self.choice + if event[0] == wasp.EventType.DOWN: + choice = choice - 1 if choice > 0 else len(self.choices)-1 + if event[0] == wasp.EventType.UP: + choice = choice + 1 if choice < len(self.choices)-1 else 0 + self.choice = choice + + mute = wasp.watch.display.mute + mute(True) + self._update() + mute(False) + + def _update(self): + """Draw the display from scratch.""" + wasp.watch.drawable.fill() + (module, label) = self.choices[self.choice] + wasp.system.register('apps.{}.{}App'.format(module, label), watch_face=True) + wasp.system.quick_ring[0].preview() + self.si.draw() diff --git a/wasp/apps/software.py b/wasp/apps/software.py index dcc412e..cac4595 100644 --- a/wasp/apps/software.py +++ b/wasp/apps/software.py @@ -39,9 +39,7 @@ class SoftwareApp(): db = [] db.append(('alarm', factory('Alarm'))) db.append(('calc', factory('Calculator'))) - db.append(('chrono', factory('Chrono'))) - db.append(('dual_clock', factory('Dual Clock'))) - db.append(('fibonacci_clock', factory('Fibonacci Clock'))) + db.append(('faces', factory('Faces'))) db.append(('gameoflife', factory('Game Of Life'))) db.append(('musicplayer', factory('Music Player'))) db.append(('play2048', factory('Play 2048'))) @@ -50,7 +48,6 @@ class SoftwareApp(): db.append(('testapp', factory('Test'))) db.append(('timer', factory('Timer'))) db.append(('weather', factory('Weather'))) - db.append(('word_clock', factory('Word Clock'))) # Get the initial state for the checkboxes for _, checkbox in db: diff --git a/wasp/boards/manifest_240x240.py b/wasp/boards/manifest_240x240.py index 2f59436..b8e7fc9 100644 --- a/wasp/boards/manifest_240x240.py +++ b/wasp/boards/manifest_240x240.py @@ -8,6 +8,7 @@ manifest = ( 'apps/clock.py', 'apps/chrono.py', 'apps/dual_clock.py', + 'apps/faces.py', 'apps/fibonacci_clock.py', 'apps/flashlight.py', 'apps/gameoflife.py', diff --git a/wasp/boards/simulator/test_qa.py b/wasp/boards/simulator/test_qa.py index dd19af6..7f38995 100644 --- a/wasp/boards/simulator/test_qa.py +++ b/wasp/boards/simulator/test_qa.py @@ -3,7 +3,7 @@ import wasp import importlib import os -EXCLUDE = ('Notifications', 'Template', 'Demo') +EXCLUDE = ('Notifications', 'Template', 'Demo', 'Faces') def test_README(constructor): if constructor.NAME in EXCLUDE: diff --git a/wasp/wasp.py b/wasp/wasp.py index eaf3737..442d677 100644 --- a/wasp/wasp.py +++ b/wasp/wasp.py @@ -23,6 +23,7 @@ import watch import widgets from apps.clock import ClockApp +from apps.faces import FacesApp from apps.heart import HeartApp from apps.launcher import LauncherApp from apps.pager import PagerApp, CrashApp, NotificationApp @@ -153,8 +154,10 @@ class Manager(): (StepCounterApp, True), (StopwatchApp, True), (HeartApp, True), + (FacesApp, False), + (SettingsApp, False), (SoftwareApp, False), - (SettingsApp, False) ): + ): try: a = app()