Added the GadgetBridge notification popup (Work In Progress), removed the compass app update code from the loop, plus other ongoing code cleanup/rework of this file

This commit is contained in:
anschrammh 2023-10-19 08:14:37 +02:00
parent af71c44af4
commit bf9816457b
3 changed files with 480 additions and 31 deletions

View File

@ -13,6 +13,7 @@
#include "find_my_phone_screen.h" #include "find_my_phone_screen.h"
#include "music_player_screen.h" #include "music_player_screen.h"
#include "settings_screen.h" #include "settings_screen.h"
#include "notification_screen.h"
#include "watch_peripherals.h" #include "watch_peripherals.h"
#include "watch_settings.h" #include "watch_settings.h"
#include "firmware_version.h" #include "firmware_version.h"
@ -38,6 +39,7 @@ MenuScreen_t menuScreen;
CompassScreen_t compassScreen; CompassScreen_t compassScreen;
FindMyPhoneScreen_t findMyPhoneScreen; FindMyPhoneScreen_t findMyPhoneScreen;
MusicPlayerScreen_t musicPlayerScreen; MusicPlayerScreen_t musicPlayerScreen;
NotificationScreen_t notificationScreen;
SettingsScreen_t settingsScreen; SettingsScreen_t settingsScreen;
@ -362,7 +364,7 @@ static uint16_t angle_with_offset(uint16_t angle, uint16_t offset)
return (angle + offset) >= 360 ? angle + offset - 360 : angle + offset; return (angle + offset) >= 360 ? angle + offset - 360 : angle + offset;
} }
static void parser_event_cb(const gadget_bridge_event_data_t *gadget_bridge_event_data) static void parser_event_cb(gadget_bridge_event_data_t *gadget_bridge_event_data)
{ {
APP_LOG_INFO("[GB]Event of type : %s\n", gadget_bridge_event_type_2_str(gadget_bridge_event_data->event_type)); APP_LOG_INFO("[GB]Event of type : %s\n", gadget_bridge_event_type_2_str(gadget_bridge_event_data->event_type));
@ -396,6 +398,23 @@ static void parser_event_cb(const gadget_bridge_event_data_t *gadget_bridge_even
} }
music_player_screen_set_music_position(&musicPlayerScreen, gadget_bridge_event_data->music_state.position_in_seconds); music_player_screen_set_music_position(&musicPlayerScreen, gadget_bridge_event_data->music_state.position_in_seconds);
break; break;
case GADGET_BRIDGE_EVENT_TYPE_NOTIFY:
{
// Let's retrieve the time from the watch :
struct tm date_time;
tls_get_rtc(&date_time);
notification_screen_notify(&notificationScreen, gadget_bridge_event_data->notification.handle,
mktime(&date_time),
gadget_bridge_event_data->notification.notification_type,
gadget_bridge_event_data->notification.title,
gadget_bridge_event_data->notification.body);
//As we don't want the title and the body strings to be freed after the callback returns, let's set them to NULL
gadget_bridge_event_data->notification.title = NULL;
gadget_bridge_event_data->notification.body = NULL;
}
break;
default: default:
APP_LOG_INFO("Not yet handled\n"); APP_LOG_INFO("Not yet handled\n");
} }
@ -481,6 +500,50 @@ static void scan_result_cb(void)
tls_mem_free(buffer); tls_mem_free(buffer);
} }
static void compass_screen_on_state_change_cb(CompassScreenState_e compassScreenState)
{
switch(compassScreenState)
{
case COMPASS_SCREEN_STATE_OPENED:
watch_peripherals_magnetometer_power_mode_set(QMC5883L_Mode_Control_Continuous);
break;
case COMPASS_SCREEN_STATE_CLOSED:
default:
watch_peripherals_magnetometer_power_mode_set(QMC5883L_Mode_Control_Standby);
break;
}
}
static void settings_screen_on_state_change_cb(SettingsScreenState_e settingsScreenState, SettingsScreenCategory_e settingsScreenCategory)
{
switch(settingsScreenState)
{
case SETTINGS_SCREEN_STATE_OPENED:
if(settingsScreenCategory == SETTINGS_SCREEN_CATEGORY_ABOUT)
{
watch_peripherals_magnetometer_power_mode_set(QMC5883L_Mode_Control_Continuous);
}
break;
case SETTINGS_SCREEN_STATE_CLOSED:
if(settingsScreenCategory == SETTINGS_SCREEN_CATEGORY_ABOUT)
{
watch_peripherals_magnetometer_power_mode_set(QMC5883L_Mode_Control_Standby);
}
break;
default:
break;
}
}
static float bmp_temperature = 0;
static void compass_screen_azimuth_and_temperature_cb(uint16_t *azimuth, bool *refreshAzimuth, float *temperature, bool *refreshTemperature)
{
uint16_t sensor_azimuth = watch_peripherals_magnetometer_azimuth_read(refreshAzimuth);
*azimuth = angle_with_offset(sensor_azimuth, 270);
*temperature = bmp_temperature;
}
static void sendFindMyPhoneBLECommandCb(bool findMyPhone) static void sendFindMyPhoneBLECommandCb(bool findMyPhone)
{ {
gadget_bridge_send_find_phone(findMyPhone); gadget_bridge_send_find_phone(findMyPhone);
@ -514,7 +577,7 @@ void gfx_task(void *param)
/* Let's init all the watch's sensors */ /* Let's init all the watch's sensors */
watch_peripherals_pressure_sensor_init(); watch_peripherals_pressure_sensor_init();
watch_peripherals_magnetometer_init(); watch_peripherals_magnetometer_init();
watch_peripherals_magnetometer_calibration_data_set(-3120, 1050, 1140, 5200, 1200, 3200, 30.0); watch_peripherals_magnetometer_calibration_data_set(-7200, -2700, -1300, 3300, 1200, 3200, 30.0);
watch_peripherals_accelerometer_init(); watch_peripherals_accelerometer_init();
watch_peripherals_accelerometer_wrist_wakeup_enable(persistency_get_settings()->display.display_wrist_wakeup); watch_peripherals_accelerometer_wrist_wakeup_enable(persistency_get_settings()->display.display_wrist_wakeup);
watch_peripherals_accelerometer_step_counter_enable(true); watch_peripherals_accelerometer_step_counter_enable(true);
@ -524,7 +587,7 @@ void gfx_task(void *param)
APP_LOG_DEBUG("MAX3010X enable temp int %d", MAX3010X_enable_die_temperature_int(true));*/ APP_LOG_DEBUG("MAX3010X enable temp int %d", MAX3010X_enable_die_temperature_int(true));*/
/* Make the first battery voltage reading here */ /* Make the first battery voltage reading here */
_battery_stats.battery_voltage = watch_peripherals_get_battery_voltage(battery_unit_mv); _battery_stats.battery_voltage = watch_peripherals_get_battery_voltage(BATTERY_UNIT_MV);
_battery_stats.battery_percentage = battery_voltage_to_percentage(_battery_stats.battery_voltage); _battery_stats.battery_percentage = battery_voltage_to_percentage(_battery_stats.battery_voltage);
ble_service_set_battery_value(_battery_stats.battery_percentage); ble_service_set_battery_value(_battery_stats.battery_percentage);
@ -553,6 +616,8 @@ void gfx_task(void *param)
watch_face_init(&watchFace); watch_face_init(&watchFace);
menu_screen_init(&menuScreen); menu_screen_init(&menuScreen);
compass_screen_init(&compassScreen); compass_screen_init(&compassScreen);
compass_screen_register_on_state_change_cb(&compassScreen, &(compass_screen_on_state_change_cb));
compass_screen_register_azimuth_and_temperature_cb(&compassScreen, &(compass_screen_azimuth_and_temperature_cb));
find_my_phone_screen_init(&findMyPhoneScreen); find_my_phone_screen_init(&findMyPhoneScreen);
find_my_phone_screen_register_BLE_command_send_cb(&findMyPhoneScreen, &(sendFindMyPhoneBLECommandCb)); find_my_phone_screen_register_BLE_command_send_cb(&findMyPhoneScreen, &(sendFindMyPhoneBLECommandCb));
@ -561,7 +626,10 @@ void gfx_task(void *param)
music_player_screen_register_music_playback_control_cb(&musicPlayerScreen, &(sendMusicPlaybackBLECommandCb)); music_player_screen_register_music_playback_control_cb(&musicPlayerScreen, &(sendMusicPlaybackBLECommandCb));
music_player_screen_register_music_player_time_ref_ms_cb(&musicPlayerScreen, &(elapsed_ms)); music_player_screen_register_music_player_time_ref_ms_cb(&musicPlayerScreen, &(elapsed_ms));
notification_screen_init(&notificationScreen);
settings_screen_init(&settingsScreen); settings_screen_init(&settingsScreen);
settings_screen_register_on_state_change_cb(&settingsScreen, &(settings_screen_on_state_change_cb));
settings_screen_register_API_interface(&settingsScreen, &settingsScreenAPIInterface); settings_screen_register_API_interface(&settingsScreen, &settingsScreenAPIInterface);
watch_face_register_date_time_cb(&watchFace, &(date_time_cb)); watch_face_register_date_time_cb(&watchFace, &(date_time_cb));
@ -584,8 +652,6 @@ void gfx_task(void *param)
/* Enable WiFi hotspot scanning for antenna performance test purposes */ /* Enable WiFi hotspot scanning for antenna performance test purposes */
//tls_wifi_scan_result_cb_register(&(scan_result_cb)); //tls_wifi_scan_result_cb_register(&(scan_result_cb));
float temperature = 0;
float pressure = 0; float pressure = 0;
uint32_t ble_info_update_ms = 0; uint32_t ble_info_update_ms = 0;
@ -593,29 +659,12 @@ void gfx_task(void *param)
for(;;) for(;;)
{ {
/* Actions to perform when the watch is active only */ /* Actions to perform when the watch is active only */
if(!_is_in_ble_sleep_mode) if(!_is_in_ble_sleep_mode)
{ {
lv_timer_handler(); lv_timer_handler();
if(compass_screen_is_in_use(&compassScreen))
{
bool is_data_available = false;
uint16_t azimuth = watch_peripherals_magnetometer_azimuth_read(&is_data_available);
if(is_data_available)
{
/*
QMC5883L_MData_t MDataRaw = watch_peripherals_magnetometer_raw_data_read();
APP_LOG_TRACE("X %d Y %d Z %d", MDataRaw.MFieldX, MDataRaw.MFieldY, MDataRaw.MFieldZ);
*/
compass_screen_set_azimuth(&compassScreen, angle_with_offset(azimuth, 270));
}
compass_screen_set_temperature(&compassScreen, temperature);
}
/* Throttle CPU freq down when inactive to save power or to increase responsiveness */ /* Throttle CPU freq down when inactive to save power or to increase responsiveness */
tls_sys_clk clk; tls_sys_clk clk;
tls_sys_clk_get(&clk); tls_sys_clk_get(&clk);
@ -624,7 +673,7 @@ void gfx_task(void *param)
if(clk.cpuclk != 40) if(clk.cpuclk != 40)
{ {
tls_sys_clk_set(CPU_CLK_40M); tls_sys_clk_set(CPU_CLK_40M);
APP_LOG_DEBUG("CPU 40Mhz"); APP_LOG_DEBUG("CPU 40MHz");
} }
} }
else else
@ -632,7 +681,7 @@ void gfx_task(void *param)
if(clk.cpuclk != 160) if(clk.cpuclk != 160)
{ {
tls_sys_clk_set(CPU_CLK_160M); tls_sys_clk_set(CPU_CLK_160M);
APP_LOG_DEBUG("CPU 160Mhz"); APP_LOG_DEBUG("CPU 160MHz");
} }
} }
@ -692,7 +741,6 @@ void gfx_task(void *param)
/* On wake up, we force the watch face to sync up with the rtc /!\ RTC update delay WTF ? */ /* On wake up, we force the watch face to sync up with the rtc /!\ RTC update delay WTF ? */
tls_os_time_delay(1); tls_os_time_delay(1);
watch_face_force_sync(&watchFace); watch_face_force_sync(&watchFace);
watch_peripherals_magnetometer_power_mode_set(QMC5883L_Mode_Control_Continuous);
lcd_sleep(&LCDConfig, false); lcd_sleep(&LCDConfig, false);
lcd_on(&LCDConfig, false); lcd_on(&LCDConfig, false);
@ -707,12 +755,12 @@ void gfx_task(void *param)
{ {
main_data_update = elapsed_ms(); main_data_update = elapsed_ms();
pressure = watch_peripherals_pressure_sensor_get_pressure(&temperature); pressure = watch_peripherals_pressure_sensor_get_pressure(&bmp_temperature);
_battery_stats.battery_voltage = watch_peripherals_get_battery_voltage(battery_unit_mv); _battery_stats.battery_voltage = watch_peripherals_get_battery_voltage(BATTERY_UNIT_MV);
_battery_stats.battery_percentage = battery_voltage_to_percentage(_battery_stats.battery_voltage); _battery_stats.battery_percentage = battery_voltage_to_percentage(_battery_stats.battery_voltage);
APP_LOG_DEBUG("GFX thread, temp : %0.2f °C, press : %0.2f hPa, battery(%s) : %u mV <-> %u %%", APP_LOG_DEBUG("GFX thread, temp : %0.2f °C, press : %0.2f hPa, battery(%s) : %u mV <-> %u %%",
temperature, bmp_temperature,
pressure/100, pressure/100,
battery_controller_status_2_str(watch_peripherals_get_battery_controller_status()), battery_controller_status_2_str(watch_peripherals_get_battery_controller_status()),
_battery_stats.battery_voltage, _battery_stats.battery_voltage,
@ -726,7 +774,7 @@ void gfx_task(void *param)
{ {
_interrupts_statuses.battery_controller_status = false; _interrupts_statuses.battery_controller_status = false;
//Let's refresh the battery percentage as well: //Let's refresh the battery percentage as well:
_battery_stats.battery_voltage = watch_peripherals_get_battery_voltage(battery_unit_mv); _battery_stats.battery_voltage = watch_peripherals_get_battery_voltage(BATTERY_UNIT_MV);
_battery_stats.battery_percentage = battery_voltage_to_percentage(_battery_stats.battery_voltage); _battery_stats.battery_percentage = battery_voltage_to_percentage(_battery_stats.battery_voltage);
ble_service_set_battery_value(_battery_stats.battery_percentage); ble_service_set_battery_value(_battery_stats.battery_percentage);
watch_face_set_battery_indicator(&watchFace, _battery_stats.battery_percentage, watch_peripherals_get_battery_controller_status()); watch_face_set_battery_indicator(&watchFace, _battery_stats.battery_percentage, watch_peripherals_get_battery_controller_status());
@ -762,7 +810,6 @@ void gfx_task(void *param)
lv_port_tick_start(); lv_port_tick_start();
watch_face_force_sync(&watchFace); watch_face_force_sync(&watchFace);
watch_peripherals_magnetometer_power_mode_set(QMC5883L_Mode_Control_Continuous);
lcd_sleep(&LCDConfig, false); lcd_sleep(&LCDConfig, false);
lcd_on(&LCDConfig, false); lcd_on(&LCDConfig, false);

View File

@ -0,0 +1,331 @@
#include "notification_screen.h"
#include <stdio.h>
#include <stdlib.h>
#include "wm_mem.h"
/* Internal API to manage notification objects */
bool _notification_list_is_empty(NotificationDataList_t notificationList);
void _notification_list_add_head(NotificationDataList_t *notificationList, NotificationData_t *notification);
NotificationData_t *_notification_list_remove_tail(NotificationDataList_t *notificationList);
uint8_t _notification_list_count(NotificationDataList_t notificationList);
void _notification_list_debug(NotificationDataList_t notificationList);
/* Internal UI API */
void _display_message_notification(NotificationScreen_t * const notificationScreen, const NotificationData_t *notification);
void _notification_popup_destroy(NotificationScreen_t * const notificationScreen);
const char *_notification_type_to_char(NotificationType_e notificationType);
const char *_notification_timestamp_to_date(time_t timestamp);
static void notification_scrolled_event_cb(lv_event_t *e)
{
NotificationScreen_t *notificationScreen = lv_event_get_user_data(e);
//If we didn't scroll down enough, make the notif pop again
if(lv_obj_get_scroll_y(e->target) >= 83)
{
lv_obj_scroll_to_y(e->target, 166, LV_ANIM_ON);
}
//Else we know that the user wants to close the notification, so we do it with an animation to reach 0
else if(lv_obj_get_scroll_y(e->target) != 0)
{
lv_obj_scroll_to_y(e->target, 0, LV_ANIM_ON);
}
else //We scrolled enough to tell that we saw the notification and that we want to close it
{
LV_LOG_USER("Notification closed");
_notification_popup_destroy(notificationScreen);
if(notificationScreen->notificationOnStateChangeCb) notificationScreen->notificationOnStateChangeCb(NOTIFICATION_STATE_CLEARED);
}
}
void notification_screen_init(NotificationScreen_t * const notificationScreen)
{
if(!notificationScreen)
{
LV_LOG_ERROR("NULL pointer given !");
return;
}
memset(notificationScreen, 0, sizeof(NotificationScreen_t));
//Let's initialize the list of free notifications from the pool
for(uint8_t i = 0; i < MAX_NOTIFICATIONS_COUNT; i++)
_notification_list_add_head(&notificationScreen->freeNotificationList, notificationScreen->notificationPool + i);
}
void notification_screen_register_on_state_change_cb(NotificationScreen_t * const notificationScreen, NotificationOnStateChangeCb_t notificationOnStateChangeCb)
{
if(!notificationScreen)
{
LV_LOG_ERROR("NULL pointer given !");
return;
}
notificationScreen->notificationOnStateChangeCb = notificationOnStateChangeCb;
}
void notification_screen_notify(NotificationScreen_t * const notificationScreen, uint32_t handle, time_t dateOfArrival, NotificationType_e notificationType, char * title, char * body)
{
if(!notificationScreen)
{
LV_LOG_ERROR("NULL pointer given !");
return;
}
NotificationData_t *notification = NULL;
//We try to get a notification from the free list.
if((notification = _notification_list_remove_tail(&notificationScreen->freeNotificationList)) == NULL)
{
//If that fails, we reuse the oldest notification from the used list.
if((notification = _notification_list_remove_tail(&notificationScreen->notificationList)) == NULL)
{
//That's bad
LV_LOG_ERROR("Unable to find a notification to reuse !");
return;
}
//Don't forget to free the allocated strings if not already done
tls_mem_free(notification->title);
tls_mem_free(notification->body);
}
notification->handle = handle;
notification->dateOfArrival = dateOfArrival;
notification->type = notificationType;
notification->title = title;
notification->body = body;
notification->read = false;
_notification_list_add_head(&notificationScreen->notificationList, notification);
switch(notificationType)
{
case NOTIFICATION_TYPE_CALL:
break;
default:
_display_message_notification(notificationScreen, notification);
if(notificationScreen->notificationOnStateChangeCb) notificationScreen->notificationOnStateChangeCb(NOTIFICATION_STATE_DISPLAYED);
}
}
void notification_screen_destroy(NotificationScreen_t * const notificationScreen)
{
if(!notificationScreen)
{
LV_LOG_ERROR("NULL pointer given !");
return;
}
}
bool _notification_list_is_empty(NotificationDataList_t notificationList)
{
return notificationList == NULL;
}
void _notification_list_add_head(NotificationDataList_t *notificationList, NotificationData_t *notification)
{
if(!notificationList || !notification) return;
if(_notification_list_is_empty(*notificationList))
*notificationList = notification;
else
{
notification->next = *notificationList;
*notificationList = notification;
}
}
NotificationData_t *_notification_list_remove_tail(NotificationDataList_t *notificationList)
{
if(!notificationList) return NULL;
if(_notification_list_is_empty(*notificationList)) return NULL;
NotificationData_t *toReturn = NULL;
//There is only one item in the list
if((*notificationList)->next == NULL)
{
toReturn = *notificationList;
*notificationList = NULL;
return toReturn;
}
NotificationDataList_t cursor = *notificationList;
while(!_notification_list_is_empty(cursor->next->next))
{
cursor = cursor->next;
}
toReturn = cursor->next;
cursor->next = NULL;
return toReturn;
}
uint8_t _notification_list_count(NotificationDataList_t notificationList)
{
uint8_t count = 0;
while(!_notification_list_is_empty(notificationList))
{
notificationList = notificationList->next;
count++;
}
return count;
}
void _display_message_notification(NotificationScreen_t * const notificationScreen, const NotificationData_t *notification)
{
//Create and display a graphical widget containing the notification
lv_obj_t *notification_display = lv_layer_top();
if(!notification_display)
{
LV_LOG_ERROR("Could not retrieve sys layer !");
return;
}
/*We can have two cases :
1) No notification is currently being displayed, so we create the needed UI objects
2) A notification is currently being shown and all UI elements were already created / allocated
*/
if(lv_obj_get_child_cnt(notification_display) == 0)
{
//Only allow to scroll down and not up (LV_DIR_TOP makes it, is it a bug ?)
lv_obj_set_scroll_dir(notification_display, LV_DIR_TOP);
lv_obj_add_event_cb(notification_display, &(notification_scrolled_event_cb), LV_EVENT_SCROLL_END, notificationScreen);
lv_obj_t *main_notification = lv_obj_create(notification_display);
lv_obj_set_size(main_notification, 202, 166);
lv_obj_set_style_pad_all(main_notification, 0, LV_PART_MAIN);
lv_obj_set_style_pad_top(main_notification, 10, LV_PART_MAIN);
lv_obj_set_style_border_width(main_notification, 0, LV_PART_MAIN);
lv_obj_set_style_radius(main_notification, 30, LV_PART_MAIN);
lv_obj_align(main_notification, LV_ALIGN_BOTTOM_MID,0,166);
lv_obj_set_style_bg_color(main_notification, lv_color_make(52, 93, 106), LV_PART_MAIN);
//lv_obj_set_style_opa(main_notification, 240, LV_PART_MAIN); //Opacity is too heavy on ram :-(
//Create the title, type and date labels
if(notificationScreen->type_label)
{
LV_LOG_ERROR("type_label should be NULL here !");
lv_obj_del(notificationScreen->type_label);
notificationScreen->type_label = NULL;
}
notificationScreen->type_label = lv_label_create(main_notification);
lv_obj_set_style_text_color(notificationScreen->type_label, lv_color_white(), LV_PART_MAIN);
lv_obj_set_style_pad_left(notificationScreen->type_label, 10, LV_PART_MAIN);
lv_label_set_long_mode(notificationScreen->type_label, LV_LABEL_LONG_SCROLL_CIRCULAR);
lv_obj_set_style_anim_speed(notificationScreen->type_label, 10, LV_PART_MAIN);
lv_obj_set_width(notificationScreen->type_label, lv_pct(27));
lv_label_set_text(notificationScreen->type_label, _notification_type_to_char(notification->type));
if(notificationScreen->title_label)
{
LV_LOG_ERROR("title_label should be NULL here !");
lv_obj_del(notificationScreen->title_label);
notificationScreen->title_label = NULL;
}
notificationScreen->title_label = lv_label_create(main_notification);
lv_obj_set_style_text_color(notificationScreen->title_label, lv_color_white(), LV_PART_MAIN);
lv_obj_align(notificationScreen->title_label, LV_ALIGN_TOP_MID, 0, 0);
lv_obj_set_style_anim_speed(notificationScreen->title_label, 10, LV_PART_MAIN);
lv_label_set_long_mode(notificationScreen->title_label, LV_LABEL_LONG_SCROLL_CIRCULAR);
lv_obj_set_width(notificationScreen->title_label, lv_pct(40));
lv_label_set_text_static(notificationScreen->title_label, notification->title);
if(notificationScreen->date_label)
{
LV_LOG_ERROR("date_label should be NULL here !");
lv_obj_del(notificationScreen->date_label);
notificationScreen->date_label = NULL;
}
notificationScreen->date_label = lv_label_create(main_notification);
lv_obj_set_style_pad_right(notificationScreen->date_label, 10, LV_PART_MAIN);
lv_obj_set_style_text_color(notificationScreen->date_label, lv_color_white(), LV_PART_MAIN);
lv_obj_align(notificationScreen->date_label, LV_ALIGN_TOP_RIGHT, 0, 0);
lv_label_set_text(notificationScreen->date_label, _notification_timestamp_to_date(notification->dateOfArrival));
//Create the sub-area in the notification
lv_obj_t *sub_area = lv_obj_create(main_notification);
lv_obj_set_style_pad_all(sub_area, 10, LV_PART_MAIN);
lv_obj_set_style_border_width(sub_area, 0, LV_PART_MAIN);
lv_obj_set_style_radius(sub_area, 0, LV_PART_MAIN);
lv_obj_set_width(sub_area, 202);
lv_obj_set_height(sub_area, 136);
lv_obj_align(sub_area, LV_ALIGN_BOTTOM_MID, 0, 0);
lv_obj_set_style_bg_color(sub_area, lv_color_make(22, 52, 62), LV_PART_MAIN);
//Create the main text
if(notificationScreen->body_label)
{
LV_LOG_ERROR("body_label should be NULL here !");
lv_obj_del(notificationScreen->body_label);
notificationScreen->body_label = NULL;
}
notificationScreen->body_label = lv_label_create(sub_area);
lv_obj_set_width(notificationScreen->body_label, 182);
lv_obj_set_style_text_color(notificationScreen->body_label, lv_color_white(), LV_PART_MAIN);
lv_obj_set_style_pad_all(notificationScreen->body_label, 0, LV_PART_MAIN);
lv_obj_set_style_pad_bottom(notificationScreen->body_label, 40, LV_PART_MAIN);
lv_label_set_long_mode(notificationScreen->body_label, LV_LABEL_LONG_WRAP);
lv_label_set_text_static(notificationScreen->body_label, notification->body);
lv_obj_scroll_to_y(notification_display, 166, LV_ANIM_ON);
}
else
{
LV_LOG_USER("A notification is already being displayed, updating it's content !");
//We just have to update the notification content
lv_label_set_text_static(notificationScreen->type_label, _notification_type_to_char(notification->type));
lv_label_set_text_static(notificationScreen->title_label, notification->title);
lv_label_set_text_static(notificationScreen->date_label, _notification_timestamp_to_date(notification->dateOfArrival));
lv_label_set_text_static(notificationScreen->body_label, notification->body);
}
}
void _notification_popup_destroy(NotificationScreen_t * const notificationScreen)
{
lv_obj_clean(lv_layer_top());
notificationScreen->type_label = NULL;
notificationScreen->title_label = NULL;
notificationScreen->date_label = NULL;
notificationScreen->body_label = NULL;
}
const char *_notification_type_to_char(NotificationType_e notificationType)
{
switch(notificationType)
{
case NOTIFICATION_TYPE_SMS:
return "Sms";
case NOTIFICATION_TYPE_EMAIL:
return "Email";
case NOTIFICATION_TYPE_WHATSAPP:
return "WhatsApp";
case NOTIFICATION_TYPE_GADGET_BRIDGE:
return "GadgetBridge";
case NOTIFICATION_TYPE_UNKNOWN:
default:
return "Unknown";
}
}
const char *_notification_timestamp_to_date(time_t timestamp)
{
static char date[9]; //Ex 7:23PM
struct tm *time = gmtime(&timestamp);
sprintf(date, "%s%d:%s%d", time->tm_hour > 10 ? "" : "0", time->tm_hour, time->tm_min > 10 ? "" : "0", time->tm_min);
return date;
}
void _notification_list_debug(NotificationDataList_t notificationList)
{
while(!_notification_list_is_empty(notificationList))
{
LV_LOG_USER("Notification handle :%u", notificationList->handle);
notificationList = notificationList->next;
}
}

View File

@ -0,0 +1,71 @@
#ifndef NOTIFICATION_SCREEN_H
#define NOTIFICATION_SCREEN_H
#include "lvgl.h"
#include <time.h>
/* The maximum number of notifications which will be stored before the oldest one gets discarded
to make room for the new one.
*/
#define MAX_NOTIFICATIONS_COUNT (10)
typedef enum NotificationType
{
NOTIFICATION_TYPE_SMS = 0,
NOTIFICATION_TYPE_EMAIL,
NOTIFICATION_TYPE_WHATSAPP,
NOTIFICATION_TYPE_GADGET_BRIDGE,
NOTIFICATION_TYPE_UNKNOWN,
/* This enum value has no match in GadgetBridge's enum */
NOTIFICATION_TYPE_CALL,
} NotificationType_e;
typedef enum NotificationState
{
NOTIFICATION_STATE_CLEARED = 0,
NOTIFICATION_STATE_DISPLAYED = 1,
} NotificationState_e;
typedef struct NotificationData
{
struct NotificationData *next;
uint32_t handle;
time_t dateOfArrival;
char *title;
char *body;
NotificationType_e type;
bool read; // Has the notification been read (aka closed by the user)
} NotificationData_t, *NotificationDataList_t;
typedef void (*NotificationOnStateChangeCb_t)(NotificationState_e notificationState);
typedef struct NotificationScreen
{
//Can be erased attributes
lv_obj_t *display;
//Should not be erased attributes
//Notification UI object
lv_obj_t *type_label;
lv_obj_t *title_label;
lv_obj_t *date_label;
lv_obj_t *body_label;
NotificationOnStateChangeCb_t notificationOnStateChangeCb;
//Notification history data structure
NotificationData_t notificationPool[MAX_NOTIFICATIONS_COUNT];
NotificationDataList_t notificationList; //Actual notifications
NotificationDataList_t freeNotificationList; //Free notification object pool
} NotificationScreen_t;
void notification_screen_init(NotificationScreen_t * const notificationScreen);
void notification_screen_register_on_state_change_cb(NotificationScreen_t * const notificationScreen, NotificationOnStateChangeCb_t notificationOnStateChangeCb);
void notification_screen_notify(NotificationScreen_t * const notificationScreen, uint32_t handle, time_t dateOfArrival, NotificationType_e notificationType, char * title, char * body);
void notification_screen_create(NotificationScreen_t * const notificationScreen);
void notification_screen_destroy(NotificationScreen_t * const notificationScreen);
#endif // NOTIFICATION_SCREEN_H