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:
parent
af71c44af4
commit
bf9816457b
@ -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(¬ificationScreen, 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(¬ificationScreen);
|
||||||
|
|
||||||
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);
|
||||||
|
331
src/W800_SDK_v1.00.10/app/gfx/notification_screen.c
Normal file
331
src/W800_SDK_v1.00.10/app/gfx/notification_screen.c
Normal 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(¬ificationScreen->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(¬ificationScreen->freeNotificationList)) == NULL)
|
||||||
|
{
|
||||||
|
//If that fails, we reuse the oldest notification from the used list.
|
||||||
|
if((notification = _notification_list_remove_tail(¬ificationScreen->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(¬ificationScreen->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(×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))
|
||||||
|
{
|
||||||
|
LV_LOG_USER("Notification handle :%u", notificationList->handle);
|
||||||
|
notificationList = notificationList->next;
|
||||||
|
}
|
||||||
|
}
|
71
src/W800_SDK_v1.00.10/app/gfx/notification_screen.h
Normal file
71
src/W800_SDK_v1.00.10/app/gfx/notification_screen.h
Normal 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
|
Loading…
Reference in New Issue
Block a user