2021-10-05 13:19:44 +00:00
# Apps
2022-08-21 11:50:09 +00:00
2021-10-05 13:19:44 +00:00
This page will teach you:
2022-08-21 11:50:09 +00:00
2021-11-08 16:11:29 +00:00
- what screens and apps are in InfiniTime
2021-10-05 13:19:44 +00:00
- how to implement your own app
## Theory
2021-11-08 16:11:29 +00:00
2021-11-08 16:13:27 +00:00
The user interface of InfiniTime is made up of **screens** .
Screens that are opened from the app launcher are considered **apps** .
2023-11-11 16:56:36 +00:00
Every app in InfiniTime is its own class.
2021-12-05 16:41:01 +00:00
An instance of the class is created when the app is launched, and destroyed when the user exits the app.
2023-11-11 16:56:36 +00:00
Apps run inside the `DisplayApp` task (briefly discussed [here ](./Intro.md )).
2021-10-08 12:59:45 +00:00
Apps are responsible for everything drawn on the screen when they are running.
2023-11-11 16:56:36 +00:00
Apps can be refreshed periodically and reacts to external events (touch or button).
2021-10-05 13:19:44 +00:00
## Interface
2022-08-21 11:50:09 +00:00
2023-11-11 16:56:36 +00:00
Every app class is declared inside the namespace `Pinetime::Applications::Screens`
and inherits
from [`Pinetime::Applications::Screens::Screen` ](https://github.com/InfiniTimeOrg/InfiniTime/blob/main/src/displayapp/screens/Screen.h ).
2021-10-06 12:30:16 +00:00
2023-11-11 16:56:36 +00:00
Each app defines its own constructor.
The constructors mostly take references to InfiniTime `Controllers` (ex: Alarm, DateTime, BLE services, Settings,...)
the app needs for its operations. The constructor is responsible for initializing the UI of the app.
2022-08-21 11:50:09 +00:00
2023-11-11 16:56:36 +00:00
The **destructor** cleans up LVGL and restores any changes (for example re-enable sleeping).
2021-10-06 12:30:16 +00:00
2023-11-11 16:56:36 +00:00
App classes can override `bool OnButtonPushed()` , `bool OnTouchEvent(TouchEvents event)`
and `bool OnTouchEvent(uint16_t x, uint16_t y)` to implement their own functionality for those events.
2022-08-21 11:50:09 +00:00
2023-11-11 16:56:36 +00:00
Apps that need to be refreshed periodically create an `lv_task` (using `lv_task_create()` )
that will call the method `Refresh()` periodically.
## App types
2024-01-06 10:28:48 +00:00
There are basically 3 types of applications : **system** apps and **user** apps and **watch faces** .
2023-11-11 16:56:36 +00:00
**System** applications are always built into InfiniTime, and InfiniTime cannot work properly without those apps.
2024-01-06 13:28:34 +00:00
Settings, notifications and the application launcher are examples of such system applications.
2023-11-11 16:56:36 +00:00
**User** applications are optionally built into the firmware. They extend the functionalities of the system.
2024-01-06 10:28:48 +00:00
**Watch faces** are very similar to the **user** apps, they are optional, but at least one must be built into the firmware.
2023-12-30 19:46:36 +00:00
2024-01-06 10:28:48 +00:00
The distinction between **system** apps, **user** apps and watch faces allows for more flexibility and customization.
This allows to easily select which user applications and watch faces must be built into the firmware
2023-11-11 16:56:36 +00:00
without overflowing the system memory.
2024-01-06 10:28:48 +00:00
## Apps and watch faces initialization
2023-11-11 16:56:36 +00:00
Apps are created by `DisplayApp` in `DisplayApp::LoadScreen()` .
This method simply call the creates an instance of the class that corresponds to the app specified in parameters.
The constructor of **system** apps is called directly. If the application is a **user** app,
the corresponding `AppDescription` is first retrieved from `userApps`
and then the function `create` is called to create an instance of the app.
2024-01-06 10:28:48 +00:00
Watch faces are handled in a very similar way as the **user** apps : they are created by `DisplayApp` in the method `DisplayApp::LoadScreen()` when the application type is `Apps::Clock` .
2023-12-30 19:46:36 +00:00
2023-11-11 16:56:36 +00:00
## User application selection at build time
The list of user applications is generated at build time by the `consteval` function `CreateAppDescriptions()`
in `UserApps.h` . This method takes the list of applications that must be built into the firmware image.
2023-12-23 19:37:20 +00:00
This list of applications is defined as a list `Apps` enum values named `UserAppTypes` in `Apps.h` .
2023-11-11 16:56:36 +00:00
For each application listed in `UserAppTypes` , an entry of type `AppDescription` is added to the array `userApps` .
This entry is created by using the information provided by a template `AppTraits`
that is customized for every user application.
Here is an example of an AppTraits customized for the Alarm application.
It defines the type of application, its icon and a function that returns an instance of the application.
```c++
template < >
struct AppTraits< Apps::Alarm > {
static constexpr Apps app = Apps::Alarm;
static constexpr const char* icon = Screens::Symbols::clock;
static Screens::Screen* Create(AppControllers& controllers) {
return new Screens::Alarm(controllers.alarmController,
controllers.settingsController.GetClockType(),
*controllers.systemTask,
controllers.motorController);
};
};
2021-11-08 16:11:29 +00:00
```
2021-10-06 12:30:16 +00:00
2023-11-11 16:56:36 +00:00
This array `userApps` is used by `DisplayApp` to create the applications and the `AppLauncher`
to list all available applications.
2021-10-05 13:19:44 +00:00
2024-01-06 10:28:48 +00:00
## Watch face selection at build time
2023-12-30 19:46:36 +00:00
2024-01-06 10:28:48 +00:00
The list of available watch faces is also generated at build time by the `consteval`
2023-12-30 19:46:36 +00:00
function `CreateWatchFaceDescriptions()` in `UserApps.h` in the same way as the **user** apps.
2024-01-06 10:28:48 +00:00
Watch faces must declare a `WatchFaceTraits` so that the corresponding `WatchFaceDescription` can be generated.
2023-12-30 19:46:36 +00:00
Here is an example of `WatchFaceTraits` :
```c++
template < >
struct WatchFaceTraits< WatchFace::Analog > {
static constexpr WatchFace watchFace = WatchFace::Analog;
static constexpr const char* name = "Analog face";
static Screens::Screen* Create(AppControllers& controllers) {
return new Screens::WatchFaceAnalog(controllers.dateTimeController,
controllers.batteryController,
controllers.bleController,
controllers.notificationManager,
controllers.settingsController);
};
static bool IsAvailable(Pinetime::Controllers::FS& /*filesystem*/) {
return true;
}
};
```
2021-10-05 13:19:44 +00:00
## Creating your own app
2022-08-21 11:50:09 +00:00
2023-11-11 16:56:36 +00:00
A minimal user app could look like this:
2021-11-08 16:11:29 +00:00
2021-10-05 13:19:44 +00:00
MyApp.h:
2022-08-21 11:50:09 +00:00
2021-10-05 13:19:44 +00:00
```cpp
#pragma once
2023-11-11 16:56:36 +00:00
#include "displayapp/Apps.h"
2021-10-05 13:19:44 +00:00
#include "displayapp/screens/Screen.h"
2023-11-11 16:56:36 +00:00
#include "displayapp/Controllers.h"
#include "Symbols.h"
2021-10-05 13:19:44 +00:00
2021-10-18 04:35:47 +00:00
namespace Pinetime {
2021-10-06 12:30:16 +00:00
namespace Applications {
namespace Screens {
class MyApp : public Screen {
public:
2023-11-16 17:46:25 +00:00
MyApp();
2021-10-06 12:30:16 +00:00
~MyApp() override;
2021-10-18 16:18:35 +00:00
};
2021-10-05 13:19:44 +00:00
}
2023-11-11 16:56:36 +00:00
template < >
2024-03-14 13:55:06 +00:00
struct AppTraits< Apps::MyApp > {
2023-11-11 16:56:36 +00:00
static constexpr Apps app = Apps::MyApp;
2024-03-14 15:39:36 +00:00
static constexpr const char* icon = Screens::Symbols::myApp;
2024-03-14 15:41:17 +00:00
static Screens::Screen* Create(AppControllers& controllers) {
2023-11-11 16:56:36 +00:00
return new Screens::MyApp();
}
};
2021-10-06 12:30:16 +00:00
}
2021-10-05 13:19:44 +00:00
}
```
MyApp.cpp:
2022-08-21 11:50:09 +00:00
2021-10-05 13:19:44 +00:00
```cpp
2021-11-08 16:11:29 +00:00
#include "displayapp/screens/MyApp.h"
2021-10-05 13:19:44 +00:00
using namespace Pinetime::Applications::Screens;
2023-11-16 17:46:25 +00:00
MyApp::MyApp() {
2021-11-08 16:11:29 +00:00
lv_obj_t* title = lv_label_create(lv_scr_act(), nullptr);
2021-10-06 16:29:52 +00:00
lv_label_set_text_static(title, "My test application");
2021-10-06 12:30:16 +00:00
lv_label_set_align(title, LV_LABEL_ALIGN_CENTER);
lv_obj_align(title, lv_scr_act(), LV_ALIGN_CENTER, 0, 0);
2021-10-05 13:19:44 +00:00
}
MyApp::~MyApp() {
2021-10-06 12:30:16 +00:00
lv_obj_clean(lv_scr_act());
2021-10-05 13:19:44 +00:00
}
```
2022-08-21 11:50:09 +00:00
2023-11-11 16:56:36 +00:00
Both of these files should be in [displayapp/screens/ ](/src/displayapp/screens/ ).
2021-10-08 12:59:45 +00:00
Now we have our very own app, but InfiniTime does not know about it yet.
2023-11-11 16:56:36 +00:00
The first step is to include your `MyApp.cpp` (or any new cpp files for that matter)
2021-10-08 12:59:45 +00:00
in the compilation by adding it to [CMakeLists.txt ](/CMakeLists.txt ).
2023-11-11 16:56:36 +00:00
The next step to making it launch-able is to give your app an id.
2024-03-14 13:55:06 +00:00
To do this, add an entry in the enum class `Pinetime::Applications::Apps` ([displayapp/apps/Apps.h](/src/displayapp/apps/Apps.h.in)).
2023-11-11 16:56:36 +00:00
Name this entry after your app. Add `#include "displayapp/screens/MyApp.h"`
to the file [displayapp/DisplayApp.cpp ](/src/displayapp/DisplayApp.cpp ).
If your application is a **system** application, go to the function `DisplayApp::LoadScreen`
and add another case to the switch statement.
2021-10-08 12:59:45 +00:00
The case will be the id you gave your app earlier.
If your app needs any additional arguments, this is the place to pass them.
2023-11-11 16:56:36 +00:00
If your application is a **user** application, you don't need to add anything in DisplayApp,
everything will be automatically generated for you.
The user application will also be automatically be added to the app launcher menu.
2021-10-05 13:19:44 +00:00
2023-12-23 19:43:59 +00:00
Since the list of **user** application is generated by CMake, you need to add the variable `ENABLE_USERAPPS` to the command line of CMake.
This variable must be set with a string composed of an ordered list of the **user** applications that must be built into the firmware.
The items of the list are fields from the enumeration `Apps` .
Ex : build the firmware with 3 user application : Alarm, Timer and MyApp (the application will be listed in this specific order in the application menu).
2023-12-17 16:18:25 +00:00
```cmake
2023-12-19 15:37:04 +00:00
$ cmake ... -DENABLE_USERAPPS="Apps::Alarm, Apps::Timer, Apps::MyApp" ...
2023-12-17 16:18:25 +00:00
```
2024-01-06 10:28:48 +00:00
Similarly, the list of watch faces is also generated by CMake, so you need to add the variable `ENABLE_WATCHFACES` to the command line of CMake.
It must be set with the comma separated list of watch faces that will be built into the firmware.
2023-12-30 19:46:36 +00:00
2024-01-06 10:28:48 +00:00
Ex: build the firmware with 3 watch faces : Analog, PineTimeStyle and Infineat:
2023-12-30 19:46:36 +00:00
```cmake
$ cmake ... -DENABLE_WATCHFACES="WatchFace::Analog,WatchFace::PineTimeStyle,WatchFace::Infineat" ...
```
2021-10-08 12:59:45 +00:00
You should now be able to [build ](../buildAndProgram.md ) the firmware
and flash it to your PineTime. Yay!
2021-10-06 16:29:52 +00:00
2021-10-08 12:59:45 +00:00
Please remember to pay attention to the [UI guidelines ](../ui_guidelines.md )
2021-11-08 16:11:29 +00:00
when designing an app that you want to be included in InfiniTime.