From 9e63ebd529df9b3fe69a54877d5c1537618e058c Mon Sep 17 00:00:00 2001 From: anschrammh Date: Thu, 19 Oct 2023 08:30:15 +0200 Subject: [PATCH] Updated the notification screen in the CodeBlocks LVGL simulator as work is in progress --- .../lv_port_win_codeblocks/LittlevGL.layout | 845 +++++++++--------- .../notification_screen.c | 326 ++++++- .../notification_screen.h | 58 +- 3 files changed, 769 insertions(+), 460 deletions(-) diff --git a/src/lvgl_win_sim/lv_port_win_codeblocks/LittlevGL.layout b/src/lvgl_win_sim/lv_port_win_codeblocks/LittlevGL.layout index 68da4bc..13a5671 100644 --- a/src/lvgl_win_sim/lv_port_win_codeblocks/LittlevGL.layout +++ b/src/lvgl_win_sim/lv_port_win_codeblocks/LittlevGL.layout @@ -2,373 +2,204 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + @@ -391,124 +222,39 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - + - + - + - + - + - + - + - + - + - + - - - - - - + @@ -516,14 +262,273 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/lvgl_win_sim/lv_port_win_codeblocks/notification_screen.c b/src/lvgl_win_sim/lv_port_win_codeblocks/notification_screen.c index e1bc326..5c02a96 100644 --- a/src/lvgl_win_sim/lv_port_win_codeblocks/notification_screen.c +++ b/src/lvgl_win_sim/lv_port_win_codeblocks/notification_screen.c @@ -1,15 +1,45 @@ +#include #include #include "notification_screen.h" -bool _notification_list_is_empty(NotificationList_t notificationList); +/* 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); +NotificationData_t *_notification_list_remove_by_handle(NotificationDataList_t *notificationList, uint32_t handle); +uint8_t _notification_list_count(NotificationDataList_t notificationList); +uint8_t _notification_list_unread_count(NotificationDataList_t notificationList); -void _notification_list_add_head(NotificationList_t *notificationList, Notification_t *notification); +void _notification_list_debug(NotificationDataList_t notificationList); +/* Internal UI API */ +void _display_message_notification(NotificationScreen_t * const notificationScreen, 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); -Notification_t *_notification_list_remove_tail(NotificationList_t *notificationList); - -uint8_t _notification_list_count(NotificationList_t notificationList); - -void _notification_list_debug(NotificationList_t notificationList); +static void notification_scrolled_event_cb(lv_event_t *e) +{ + NotificationScreen_t *notificationScreen = lv_event_get_user_data(e); + NotificationData_t *notification = lv_obj_get_user_data(lv_event_get_target(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); + notificationScreen->new_notification_available = false; + notification->read = true; + if(notificationScreen->notificationOnStateChangeCb) notificationScreen->notificationOnStateChangeCb(NOTIFICATION_STATE_CLEARED); + } +} void notification_screen_init(NotificationScreen_t * const notificationScreen) { @@ -19,14 +49,14 @@ void notification_screen_init(NotificationScreen_t * const notificationScreen) 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(¬ificationScreen->freeNotificationList, notificationScreen->notificationPool + i); - - notification_screen_notify(notificationScreen, 42, NOTIFICATION_TYPE_UNKNOWN, NULL, NULL); } -void notification_screen_notify(NotificationScreen_t * const notificationScreen, uint32_t handle, NotificationType_e notificationType, char * title, char * body) +void notification_screen_register_on_state_change_cb(NotificationScreen_t * const notificationScreen, NotificationOnStateChangeCb_t notificationOnStateChangeCb) { if(!notificationScreen) { @@ -34,7 +64,18 @@ void notification_screen_notify(NotificationScreen_t * const notificationScreen, return; } - Notification_t *notification = NULL; + 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(¬ificationScreen->freeNotificationList)) == NULL) @@ -53,32 +94,66 @@ void notification_screen_notify(NotificationScreen_t * const notificationScreen, } notification->handle = handle; + notification->dateOfArrival = dateOfArrival; notification->type = notificationType; notification->title = title; notification->body = body; + notification->read = false; _notification_list_add_head(¬ificationScreen->notificationList, notification); + notificationScreen->new_notification_available = true; - //Create and display a graphical widget containing the notification - lv_obj_t *notification_display = lv_layer_sys(); - - if(!notification_display) + switch(notificationType) { - LV_LOG_ERROR("Could not retrieve sys layer !"); - return; + case NOTIFICATION_TYPE_CALL: + break; + default: + _display_message_notification(notificationScreen, notification); + if(notificationScreen->notificationOnStateChangeCb) notificationScreen->notificationOnStateChangeCb(NOTIFICATION_STATE_DISPLAYED); } - /*lv_obj_t *obj = lv_obj_create(notification_display); - lv_obj_set_style_bg_color(obj, lv_palette_main(LV_PALETTE_RED), LV_PART_MAIN); - lv_obj_set_height(obj, lv_disp_get_hor_res(NULL)/2); - lv_obj_set_style_bg_opa(notification_display, 255, LV_PART_MAIN);*/ } -bool _notification_list_is_empty(NotificationList_t notificationList) +bool notification_screen_new_notification_available(NotificationScreen_t * const notificationScreen) +{ + if(!notificationScreen) + { + LV_LOG_ERROR("NULL pointer given !"); + return false; + } + + if(notificationScreen->new_notification_available) + { + notificationScreen->new_notification_available = false; + return true; + } + return false; +} + +uint8_t notification_screen_notification_count(NotificationScreen_t * const notificationScreen) +{ + return _notification_list_count(notificationScreen->notificationList); +} + +uint8_t notification_screen_unread_notification_count(NotificationScreen_t * const notificationScreen) +{ + return _notification_list_unread_count(notificationScreen->notificationList);; +} + +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(NotificationList_t *notificationList, Notification_t *notification) +void _notification_list_add_head(NotificationDataList_t *notificationList, NotificationData_t *notification) { if(!notificationList || !notification) return; @@ -91,13 +166,13 @@ void _notification_list_add_head(NotificationList_t *notificationList, Notificat } } -Notification_t *_notification_list_remove_tail(NotificationList_t *notificationList) +NotificationData_t *_notification_list_remove_tail(NotificationDataList_t *notificationList) { if(!notificationList) return NULL; if(_notification_list_is_empty(*notificationList)) return NULL; - Notification_t *toReturn = NULL; + NotificationData_t *toReturn = NULL; //There is only one item in the list if((*notificationList)->next == NULL) @@ -107,7 +182,7 @@ Notification_t *_notification_list_remove_tail(NotificationList_t *notificationL return toReturn; } - NotificationList_t cursor = *notificationList; + NotificationDataList_t cursor = *notificationList; while(!_notification_list_is_empty(cursor->next->next)) { cursor = cursor->next; @@ -118,7 +193,40 @@ Notification_t *_notification_list_remove_tail(NotificationList_t *notificationL return toReturn; } -uint8_t _notification_list_count(NotificationList_t notificationList) +NotificationData_t *_notification_list_remove_by_handle(NotificationDataList_t *notificationList, uint32_t handle) +{ + 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) + { + if((*notificationList)->handle == handle) + { + toReturn = *notificationList; + *notificationList = NULL; + } + return toReturn; + } + + NotificationDataList_t cursor = *notificationList; + while(!_notification_list_is_empty(cursor->next)) + { + if(cursor->next->handle == handle) + { + toReturn = cursor->next; + cursor->next = NULL; + return toReturn; + } + cursor = cursor->next; + } + return toReturn; +} + +uint8_t _notification_list_count(NotificationDataList_t notificationList) { uint8_t count = 0; while(!_notification_list_is_empty(notificationList)) @@ -130,7 +238,169 @@ uint8_t _notification_list_count(NotificationList_t notificationList) return count; } -void _notification_list_debug(NotificationList_t notificationList) +uint8_t _notification_list_unread_count(NotificationDataList_t notificationList) +{ + uint8_t count = 0; + while(!_notification_list_is_empty(notificationList)) + { + if(!notificationList->read)count++; + notificationList = notificationList->next; + } + + return count; +} + +void _display_message_notification(NotificationScreen_t * const notificationScreen, 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 + */ + + lv_obj_set_user_data(notification_display, notification); + + 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()); + lv_obj_remove_event_cb_with_user_data(lv_layer_top(), &(notification_scrolled_event_cb), notificationScreen); + 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(×tamp); + 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)) { diff --git a/src/lvgl_win_sim/lv_port_win_codeblocks/notification_screen.h b/src/lvgl_win_sim/lv_port_win_codeblocks/notification_screen.h index a8e75f2..2f212ce 100644 --- a/src/lvgl_win_sim/lv_port_win_codeblocks/notification_screen.h +++ b/src/lvgl_win_sim/lv_port_win_codeblocks/notification_screen.h @@ -1,8 +1,13 @@ #ifndef NOTIFICATION_SCREEN_H +#define NOTIFICATION_SCREEN_H #include "lvgl.h" +#include -#define MAX_NOTIFICATIONS_COUNT (10) //The maximum number of notifications which will be stored before the oldest one gets discarded. +/* 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 { @@ -11,32 +16,61 @@ typedef enum NotificationType 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 struct Notification +typedef enum NotificationState { - struct Notification *next; + NOTIFICATION_STATE_CLEARED = 0, + NOTIFICATION_STATE_DISPLAYED = 1, +} NotificationState_e; + +typedef struct NotificationData +{ + struct NotificationData *next; uint32_t handle; - NotificationType_e type; + time_t dateOfArrival; char *title; char *body; -} Notification_t, *NotificationList_t; + 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 + // Can be erased attributes lv_obj_t *display; - //Should not be erased attributes - Notification_t notificationPool[MAX_NOTIFICATIONS_COUNT]; - NotificationList_t notificationList; //Actual notifications - NotificationList_t freeNotificationList; //Free notiffication object pool - + // 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 + // Miscellaneous + bool new_notification_available; } NotificationScreen_t; void notification_screen_init(NotificationScreen_t * const notificationScreen); -void notification_screen_notify(NotificationScreen_t * const notificationScreen, uint32_t handle, NotificationType_e notificationType, char * title, char * body); +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); + +bool notification_screen_new_notification_available(NotificationScreen_t * const notificationScreen); + +uint8_t notification_screen_notification_count(NotificationScreen_t * const notificationScreen); + +uint8_t notification_screen_unread_notification_count(NotificationScreen_t * const notificationScreen); void notification_screen_create(NotificationScreen_t * const notificationScreen);