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
There are basically 2 types of applications : **system** apps and **user** apps.
**System** applications are always built into InfiniTime, and InfiniTime cannot work properly without those apps.
The watchfaces, settings, notifications and the application launcher are examples of such system applications.
**User** applications are optionally built into the firmware. They extend the functionalities of the system.
The distinction between **system** and **user** applications allows for more flexibility and customization.
This allows to easily select which user applications must be built into the firmware
without overflowing the system memory.
## Apps initialization
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.
## 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-17 16:18:25 +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
## 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 < >
struct AppTraits< Apps:MyApp > {
static constexpr Apps app = Apps::MyApp;
static constexpr const char* icon = Screens::Symbol::myApp;
static Screens::Screens* Create(AppController& controllers) {
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.
2021-10-08 12:59:45 +00:00
To do this, add an entry in the enum class `Pinetime::Applications::Apps` ([displayapp/Apps.h](/src/displayapp/Apps.h)).
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-19 15:37:04 +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
```
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.