Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions doc/gettingStarted/Applications.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,3 +97,9 @@ InfiniTime has 13 apps on the `main` branch at the time of writing.
![Weather UI](/doc/gettingStarted/AppsScreenshots/Weather.png)
- This app shows weather info.
- Please note that this app is not very useful without a device connected.

### Tally
![Tally UI](/doc/gettingStarted/AppsScreenshots/Tally.png)
- Tap to count, or enable shake-to-count.
- Vibrates when the counter increases.
- Enable keep awake to have your count always visible.
Binary file added doc/gettingStarted/AppsScreenshots/Tally.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -398,6 +398,7 @@ list(APPEND SOURCE_FILES
displayapp/screens/Alarm.cpp
displayapp/screens/Styles.cpp
displayapp/screens/WeatherSymbols.cpp
displayapp/screens/Tally.cpp
displayapp/Colors.cpp
displayapp/widgets/Counter.cpp
displayapp/widgets/PageIndicator.cpp
Expand Down Expand Up @@ -615,6 +616,7 @@ set(INCLUDE_FILES
displayapp/screens/Timer.h
displayapp/screens/Dice.h
displayapp/screens/Alarm.h
displayapp/screens/Tally.h
displayapp/Colors.h
displayapp/widgets/Counter.h
displayapp/widgets/PageIndicator.h
Expand Down
1 change: 1 addition & 0 deletions src/displayapp/UserApps.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include "displayapp/screens/Timer.h"
#include "displayapp/screens/Twos.h"
#include "displayapp/screens/Tile.h"
#include "displayapp/screens/Tally.h"
#include "displayapp/screens/ApplicationList.h"
#include "displayapp/screens/WatchFaceDigital.h"
#include "displayapp/screens/WatchFaceAnalog.h"
Expand Down
1 change: 1 addition & 0 deletions src/displayapp/apps/Apps.h.in
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ namespace Pinetime {
SettingChimes,
SettingShakeThreshold,
SettingBluetooth,
Tally,
Error
};

Expand Down
1 change: 1 addition & 0 deletions src/displayapp/apps/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ else ()
set(DEFAULT_USER_APP_TYPES "${DEFAULT_USER_APP_TYPES}, Apps::Navigation")
set(DEFAULT_USER_APP_TYPES "${DEFAULT_USER_APP_TYPES}, Apps::Calculator")
set(DEFAULT_USER_APP_TYPES "${DEFAULT_USER_APP_TYPES}, Apps::Weather")
set(DEFAULT_USER_APP_TYPES "${DEFAULT_USER_APP_TYPES}, Apps::Tally")
#set(DEFAULT_USER_APP_TYPES "${DEFAULT_USER_APP_TYPES}, Apps::Motion")
set(USERAPP_TYPES "${DEFAULT_USER_APP_TYPES}" CACHE STRING "List of user apps to build into the firmware")
endif ()
Expand Down
Binary file added src/displayapp/fonts/AdwaitaMono-Bold.ttf
Binary file not shown.
6 changes: 5 additions & 1 deletion src/displayapp/fonts/fonts.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,11 @@
},
{
"file": "FontAwesome5-Solid+Brands+Regular.woff",
"range": "0xf294, 0xf242, 0xf54b, 0xf21e, 0xf1e6, 0xf017, 0xf129, 0xf03a, 0xf185, 0xf560, 0xf001, 0xf3fd, 0xf1fc, 0xf45d, 0xf59f, 0xf5a0, 0xf027, 0xf028, 0xf6a9, 0xf04b, 0xf04c, 0xf048, 0xf051, 0xf095, 0xf3dd, 0xf04d, 0xf2f2, 0xf024, 0xf252, 0xf569, 0xf06e, 0xf015, 0xf00c, 0xf0f3, 0xf522, 0xf743, 0xf1ec, 0xf55a"
"range": "0xf294, 0xf242, 0xf54b, 0xf21e, 0xf1e6, 0xf017, 0xf129, 0xf03a, 0xf185, 0xf560, 0xf001, 0xf3fd, 0xf1fc, 0xf45d, 0xf59f, 0xf5a0, 0xf027, 0xf028, 0xf6a9, 0xf04b, 0xf04c, 0xf048, 0xf051, 0xf095, 0xf3dd, 0xf04d, 0xf2f2, 0xf024, 0xf252, 0xf569, 0xf06e, 0xf015, 0xf00c, 0xf0f3, 0xf522, 0xf743, 0xf1ec, 0xf55a, 0xf0dc, 0xf0e2"
},
{
"file": "AdwaitaMono-Bold.ttf",
"range": "0x1D378"
}
],
"bpp": 1,
Expand Down
3 changes: 3 additions & 0 deletions src/displayapp/screens/Symbols.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ namespace Pinetime {
static constexpr const char* sleep = "\xEE\xBD\x84";
static constexpr const char* calculator = "\xEF\x87\xAC";
static constexpr const char* backspace = "\xEF\x95\x9A";
static constexpr const char* tally = "\xF0\x9D\x8D\xB8";
static constexpr const char* sort = "\xEF\x83\x9C";
static constexpr const char* undo = "\xEF\x83\xA2";

// fontawesome_weathericons.c
// static constexpr const char* sun = "\xEF\x86\x85";
Expand Down
189 changes: 189 additions & 0 deletions src/displayapp/screens/Tally.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
#include "displayapp/screens/Tally.h"
#include "displayapp/screens/Screen.h"
#include "displayapp/screens/Symbols.h"
#include "components/settings/Settings.h"
#include "components/motion/MotionController.h"
#include "components/motor/MotorController.h"

using namespace Pinetime::Applications::Screens;

namespace {
void CountButtonEventHandler(lv_obj_t* obj, lv_event_t event) {
auto* screen = static_cast<Tally*>(obj->user_data);
if (event == LV_EVENT_CLICKED) {
screen->Increment();
}
}

void ResetButtonEventHandler(lv_obj_t* obj, lv_event_t event) {
auto* screen = static_cast<Tally*>(obj->user_data);
if (event == LV_EVENT_CLICKED) {
screen->Reset();
}
}

void ShakeButtonEventHandler(lv_obj_t* obj, lv_event_t event) {
auto* screen = static_cast<Tally*>(obj->user_data);
if (event == LV_EVENT_CLICKED) {
screen->ToggleShakeToCount();
}
}

void AwakeButtonEventHandler(lv_obj_t* obj, lv_event_t event) {
auto* screen = static_cast<Tally*>(obj->user_data);
if (event == LV_EVENT_CLICKED) {
screen->ToggleKeepAwake();
}
}
}

Tally::Tally(Controllers::MotionController& motionController,
Controllers::MotorController& motorController,
Controllers::Settings& settingsController,
System::SystemTask& systemTask)
: motionController {motionController}, motorController {motorController}, settingsController {settingsController}, wakeLock(systemTask) {

// the count is actually a button (it was the easiest way to detect taps)
countButton = lv_btn_create(lv_scr_act(), nullptr);
countButton->user_data = this;
lv_obj_set_event_cb(countButton, CountButtonEventHandler);
lv_obj_set_size(countButton, 240, 180);
lv_obj_set_style_local_bg_color(countButton, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_BLACK);
lv_obj_align(countButton, nullptr, LV_ALIGN_IN_TOP_MID, 0, 0);
countLabel = lv_label_create(lv_scr_act(), nullptr);
lv_obj_set_style_local_text_font(countLabel, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, &open_sans_light);
lv_obj_align(countLabel, nullptr, LV_ALIGN_IN_TOP_MID, 0, 25);

messageLabel = lv_label_create(lv_scr_act(), nullptr);
lv_obj_align(messageLabel, nullptr, LV_ALIGN_IN_BOTTOM_MID, 0, -75);
SetMessage("Tap to count");

resetButton = lv_btn_create(lv_scr_act(), nullptr);
resetButton->user_data = this;
lv_obj_set_event_cb(resetButton, ResetButtonEventHandler);
lv_obj_set_size(resetButton, 76, 60);
lv_obj_align(resetButton, nullptr, LV_ALIGN_IN_BOTTOM_LEFT, 0, 0);
resetLabel = lv_label_create(resetButton, nullptr);
lv_label_set_text_static(resetLabel, Symbols::undo);

shakeButton = lv_btn_create(lv_scr_act(), nullptr);
shakeButton->user_data = this;
lv_obj_set_event_cb(shakeButton, ShakeButtonEventHandler);
lv_obj_set_size(shakeButton, 76, 60);
lv_obj_set_style_local_bg_color(shakeButton, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_RED);
lv_obj_align(shakeButton, nullptr, LV_ALIGN_IN_BOTTOM_MID, 0, 0);
shakeLabel = lv_label_create(shakeButton, nullptr);
lv_label_set_text_static(shakeLabel, Symbols::sort);

awakeButton = lv_btn_create(lv_scr_act(), nullptr);
awakeButton->user_data = this;
lv_obj_set_event_cb(awakeButton, AwakeButtonEventHandler);
lv_obj_set_size(awakeButton, 76, 60);
lv_obj_set_style_local_bg_color(awakeButton, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_RED);
lv_obj_align(awakeButton, nullptr, LV_ALIGN_IN_BOTTOM_RIGHT, 0, 0);
awakeLabel = lv_label_create(awakeButton, nullptr);
lv_label_set_text_static(awakeLabel, Symbols::eye);

UpdateCount();

refreshTask = lv_task_create(RefreshTaskCallback, period, LV_TASK_PRIO_MID, this);
}

Tally::~Tally() {
ShakeToWakeDisable();
lv_task_del(refreshTask);
lv_obj_clean(lv_scr_act());
}

void Tally::Refresh() {
if (shakeToCountEnabled) {
if (motionController.CurrentShakeSpeed() >= settingsController.GetShakeThreshold()) {
if (shakeToCountDelay == 0) {
shakeToCountDelay = shakeDelayTime / period;
Increment();
}
} else if (shakeToCountDelay > 0) {
shakeToCountDelay--;
}
}

if (incrementDelay > 0) {
incrementDelay--;
}

if (messageTimer > 0) {
messageTimer--;
if (messageTimer == 0) {
lv_label_set_text_static(messageLabel, "");
}
}
}

void Tally::Increment() {
if (incrementDelay <= 0) {
incrementDelay = incrementDelayTime / period;
count++;
if (count == 100) {
lv_obj_set_style_local_text_font(countLabel, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, &jetbrains_mono_76);
}
motorController.RunForDuration(80);
UpdateCount();
}
}

void Tally::Reset() {
if (count >= 100) {
lv_obj_set_style_local_text_font(countLabel, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, &open_sans_light);
}
count = 0;
UpdateCount();
}

void Tally::ToggleShakeToCount() {
shakeToCountEnabled = !shakeToCountEnabled;
if (shakeToCountEnabled) {
ShakeToWakeEnable();
} else {
ShakeToWakeDisable();
}
SetMessage(shakeToCountEnabled ? "Shake-to-count on" : "Shake-to-count off");
lv_obj_set_style_local_bg_color(shakeButton, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, shakeToCountEnabled ? LV_COLOR_GREEN : LV_COLOR_RED);
}

void Tally::ToggleKeepAwake() {
keepAwakeEnabled = !keepAwakeEnabled;
if (keepAwakeEnabled) {
wakeLock.Lock();
} else {
wakeLock.Release();
}
SetMessage(keepAwakeEnabled ? "Keep awake on" : "Keep awake off");
lv_obj_set_style_local_bg_color(awakeButton, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, keepAwakeEnabled ? LV_COLOR_GREEN : LV_COLOR_RED);
}

void Tally::UpdateCount() {
lv_label_set_text_fmt(countLabel, "%d", count);
lv_obj_realign(countLabel);
}

void Tally::ShakeToWakeEnable() {
// if shake-to-wake is not enabled, enable it while this app is open
shakeToWakeTempEnable = !settingsController.isWakeUpModeOn(Pinetime::Controllers::Settings::WakeUpMode::Shake);
if (shakeToWakeTempEnable) {
settingsController.setWakeUpMode(Pinetime::Controllers::Settings::WakeUpMode::Shake, true);
}
}

void Tally::ShakeToWakeDisable() {
// if shake-to-wake was not enabled before, disable it again
if (shakeToWakeTempEnable) {
settingsController.setWakeUpMode(Pinetime::Controllers::Settings::WakeUpMode::Shake, false);
shakeToWakeTempEnable = false;
}
}

void Tally::SetMessage(const char* text) {
lv_label_set_text_static(messageLabel, text);
lv_obj_realign(messageLabel);
messageTimer = messageTime / period;
}
73 changes: 73 additions & 0 deletions src/displayapp/screens/Tally.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
#pragma once

#include "displayapp/apps/Apps.h"
#include "displayapp/screens/Screen.h"
#include "displayapp/Controllers.h"
#include "systemtask/SystemTask.h"
#include "systemtask/WakeLock.h"
#include "Symbols.h"

namespace Pinetime {
namespace Applications {
namespace Screens {
class Tally : public Screen {
public:
Tally(Controllers::MotionController& motionController,
Controllers::MotorController& motorController,
Controllers::Settings& settingsController,
System::SystemTask& systemTask);
~Tally() override;
void Refresh() override;
void Increment();
void Reset();
void ToggleShakeToCount();
void ToggleKeepAwake();

private:
static constexpr uint16_t period = 100; // milliseconds
static constexpr uint16_t incrementDelayTime = 100; // milliseconds
static constexpr uint16_t shakeDelayTime = 200; // milliseconds
static constexpr uint16_t messageTime = 3000; // milliseconds
lv_obj_t* countButton;
lv_obj_t* countLabel;
lv_obj_t* messageLabel;
lv_obj_t* resetButton;
lv_obj_t* resetLabel;
lv_obj_t* shakeButton;
lv_obj_t* shakeLabel;
lv_obj_t* awakeButton;
lv_obj_t* awakeLabel;
uint16_t count = 0;
bool shakeToCountEnabled = false;
bool shakeToWakeTempEnable = false;
bool keepAwakeEnabled = false;
uint16_t incrementDelay = 0;
uint16_t shakeToCountDelay = 0;
uint16_t messageTimer = 0;
void UpdateCount();
void ShakeToWakeEnable();
void ShakeToWakeDisable();
void SetMessage(const char* text);

Controllers::MotionController& motionController;
Controllers::MotorController& motorController;
Controllers::Settings& settingsController;
Pinetime::System::WakeLock wakeLock;
lv_task_t* refreshTask;
};
}

template <>
struct AppTraits<Apps::Tally> {
static constexpr Apps app = Apps::Tally;
static constexpr const char* icon = Screens::Symbols::tally;

static Screens::Screen* Create(AppControllers& controllers) {
return new Screens::Tally(controllers.motionController,
controllers.motorController,
controllers.settingsController,
*controllers.systemTask);
};
};
}
}