From febc6d428422ddfd8bdadc0b75986f6becaf0de9 Mon Sep 17 00:00:00 2001 From: VerstreuteSeele Date: Mon, 19 Dec 2022 01:01:05 +0100 Subject: [PATCH] New Locale Settings API & Fix for long filenames --- .../debug/locale_test/application.fam | 11 ++ applications/debug/locale_test/locale_test.c | 102 +++++++++++++++ applications/services/gui/elements.c | 6 +- applications/services/gui/elements.h | 9 ++ .../services/gui/modules/file_browser.c | 122 ++++++++++++------ applications/services/locale/application.fam | 9 ++ applications/services/locale/locale.c | 109 ++++++++++++++++ applications/services/locale/locale.h | 53 ++++++++ applications/settings/system/application.fam | 2 +- .../settings/system/system_settings.c | 79 ++++++++++++ fbt_options.py | 2 +- firmware/targets/f7/api_symbols.csv | 14 +- firmware/targets/f7/furi_hal/furi_hal_rtc.c | 67 ++++++++-- 13 files changed, 529 insertions(+), 56 deletions(-) create mode 100644 applications/debug/locale_test/application.fam create mode 100644 applications/debug/locale_test/locale_test.c create mode 100644 applications/services/locale/application.fam create mode 100644 applications/services/locale/locale.c create mode 100644 applications/services/locale/locale.h diff --git a/applications/debug/locale_test/application.fam b/applications/debug/locale_test/application.fam new file mode 100644 index 000000000..32d065299 --- /dev/null +++ b/applications/debug/locale_test/application.fam @@ -0,0 +1,11 @@ +App( + appid="locale_test", + name="Locale Test", + apptype=FlipperAppType.DEBUG, + entry_point="locale_test_app", + cdefines=["APP_LOCALE"], + requires=["gui", "locale"], + stack_size=2 * 1024, + order=70, + fap_category="Debug", +) \ No newline at end of file diff --git a/applications/debug/locale_test/locale_test.c b/applications/debug/locale_test/locale_test.c new file mode 100644 index 000000000..594011de0 --- /dev/null +++ b/applications/debug/locale_test/locale_test.c @@ -0,0 +1,102 @@ +#include +#include +#include +#include +#include +#include + +typedef struct { + Gui* gui; + ViewDispatcher* view_dispatcher; + View* view; +} LocaleTestApp; + +static void locale_test_view_draw_callback(Canvas* canvas, void* _model) { + UNUSED(_model); + + // Prepare canvas + canvas_set_color(canvas, ColorBlack); + canvas_set_font(canvas, FontSecondary); + + FuriString* tmp_string = furi_string_alloc(); + + float temp = 25.3f; + LocaleMeasurementUnit units = locale_get_measurement_unit(); + if(units == LocaleMeasurementUnitMetric) { + furi_string_printf(tmp_string, "Temp: %5.1fC", (double)temp); + } else { + temp = locale_celsius_to_fahrenheit(temp); + furi_string_printf(tmp_string, "Temp: %5.1fF", (double)temp); + } + canvas_draw_str(canvas, 0, 10, furi_string_get_cstr(tmp_string)); + + FuriHalRtcDateTime datetime; + furi_hal_rtc_get_datetime(&datetime); + + locale_format_time(tmp_string, &datetime, locale_get_time_format(), false); + canvas_draw_str(canvas, 0, 25, furi_string_get_cstr(tmp_string)); + + locale_format_date(tmp_string, &datetime, locale_get_date_format(), "/"); + canvas_draw_str(canvas, 0, 40, furi_string_get_cstr(tmp_string)); + + furi_string_free(tmp_string); +} + +static bool locale_test_view_input_callback(InputEvent* event, void* context) { + UNUSED(event); + UNUSED(context); + return false; +} + +static uint32_t locale_test_exit(void* context) { + UNUSED(context); + return VIEW_NONE; +} + +static LocaleTestApp* locale_test_alloc() { + LocaleTestApp* app = malloc(sizeof(LocaleTestApp)); + + // Gui + app->gui = furi_record_open(RECORD_GUI); + + // View dispatcher + app->view_dispatcher = view_dispatcher_alloc(); + view_dispatcher_enable_queue(app->view_dispatcher); + view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); + + // Views + app->view = view_alloc(); + view_set_draw_callback(app->view, locale_test_view_draw_callback); + view_set_input_callback(app->view, locale_test_view_input_callback); + + view_set_previous_callback(app->view, locale_test_exit); + view_dispatcher_add_view(app->view_dispatcher, 0, app->view); + view_dispatcher_switch_to_view(app->view_dispatcher, 0); + + return app; +} + +static void locale_test_free(LocaleTestApp* app) { + furi_assert(app); + + // Free views + view_dispatcher_remove_view(app->view_dispatcher, 0); + + view_free(app->view); + view_dispatcher_free(app->view_dispatcher); + + // Close gui record + furi_record_close(RECORD_GUI); + app->gui = NULL; + + // Free rest + free(app); +} + +int32_t locale_test_app(void* p) { + UNUSED(p); + LocaleTestApp* app = locale_test_alloc(); + view_dispatcher_run(app->view_dispatcher); + locale_test_free(app); + return 0; +} \ No newline at end of file diff --git a/applications/services/gui/elements.c b/applications/services/gui/elements.c index 0f7cf73f4..785f4cbb3 100644 --- a/applications/services/gui/elements.c +++ b/applications/services/gui/elements.c @@ -532,21 +532,21 @@ void elements_bubble_str( canvas_draw_line(canvas, x2, y2, x3, y3); } -void elements_string_fit_width(Canvas* canvas, FuriString* string, uint8_t width) { + +void elements_string_fit_width_trunc(Canvas* canvas, FuriString* string, uint8_t width) { furi_assert(canvas); furi_assert(string); uint16_t len_px = canvas_string_width(canvas, furi_string_get_cstr(string)); if(len_px > width) { - width -= canvas_string_width(canvas, "..."); do { furi_string_left(string, furi_string_size(string) - 1); len_px = canvas_string_width(canvas, furi_string_get_cstr(string)); } while(len_px > width); - furi_string_cat(string, "..."); } } + void elements_text_box( Canvas* canvas, uint8_t x, diff --git a/applications/services/gui/elements.h b/applications/services/gui/elements.h index 9f155402b..eec3c7232 100644 --- a/applications/services/gui/elements.h +++ b/applications/services/gui/elements.h @@ -192,6 +192,15 @@ void elements_bubble_str( */ void elements_string_fit_width(Canvas* canvas, FuriString* string, uint8_t width); +/** Trim string buffer to fit width in pixels. If the string is longer it truncates without ellipsis (...) + * + * @param canvas Canvas instance + * @param string string to trim + * @param width max width + */ +void elements_string_fit_width_trunc(Canvas* canvas, FuriString* string, uint8_t width); + + /** Draw text box element * * @param canvas Canvas instance diff --git a/applications/services/gui/modules/file_browser.c b/applications/services/gui/modules/file_browser.c index 522d7d591..3154c1d04 100644 --- a/applications/services/gui/modules/file_browser.c +++ b/applications/services/gui/modules/file_browser.c @@ -5,8 +5,6 @@ #include #include #include "furi_hal_resources.h" -#include "m-string.h" -#include "m-algo.h" #include #include #include @@ -72,30 +70,14 @@ static void BrowserItem_t_clear(BrowserItem_t* obj) { free(obj->custom_icon_data); } } +ARRAY_DEF( -static int BrowserItem_t_cmp(const BrowserItem_t* a, const BrowserItem_t* b) { - // Back indicator comes before everything, then folders, then all other files. - if((a->type == BrowserItemTypeBack) || - (a->type == BrowserItemTypeFolder && b->type != BrowserItemTypeFolder && - b->type != BrowserItemTypeBack)) { - return -1; - } - - return furi_string_cmp(a->path, b->path); -} - -#define M_OPL_BrowserItem_t() \ - (INIT(API_2(BrowserItem_t_init)), \ - SET(API_6(BrowserItem_t_set)), \ - INIT_SET(API_6(BrowserItem_t_init_set)), \ - CLEAR(API_2(BrowserItem_t_clear)), \ - CMP(API_6(BrowserItem_t_cmp)), \ - SWAP(M_SWAP_DEFAULT), \ - EQUAL(API_6(M_EQUAL_DEFAULT))) - -ARRAY_DEF(items_array, BrowserItem_t) - -ALGO_DEF(items_array, ARRAY_OPLIST(items_array, M_OPL_BrowserItem_t())) + items_array, + BrowserItem_t, + (INIT(API_2(BrowserItem_t_init)), + SET(API_6(BrowserItem_t_set)), + INIT_SET(API_6(BrowserItem_t_init_set)), + CLEAR(API_2(BrowserItem_t_clear)))) struct FileBrowser { View* view; @@ -112,6 +94,8 @@ struct FileBrowser { FileBrowserLoadItemCallback item_callback; void* item_context; + FuriTimer* timer; + FuriString* result_path; }; @@ -126,6 +110,8 @@ typedef struct { int32_t array_offset; int32_t list_offset; + FuriString* current_filename; + const Icon* file_icon; bool hide_ext; } FileBrowserModel; @@ -139,6 +125,9 @@ static const Icon* BrowserItemIcons[] = { static void file_browser_view_draw_callback(Canvas* canvas, void* _model); static bool file_browser_view_input_callback(InputEvent* event, void* context); +static void file_browser_animate_filename_callback(void* context); + +static const char* file_browser_get_selected_filename(FileBrowserModel* model); static void browser_folder_open_cb(void* context, uint32_t item_cnt, int32_t file_idx, bool is_root); @@ -157,9 +146,17 @@ FileBrowser* file_browser_alloc(FuriString* result_path) { view_set_input_callback(browser->view, file_browser_view_input_callback); browser->result_path = result_path; + browser->timer = + furi_timer_alloc(file_browser_animate_filename_callback, FuriTimerTypePeriodic, browser); with_view_model( - browser->view, FileBrowserModel * model, { items_array_init(model->items); }, false); + browser->view, + FileBrowserModel * model, + { + items_array_init(model->items); + model->current_filename = furi_string_alloc(); + }, + false); return browser; } @@ -168,9 +165,16 @@ void file_browser_free(FileBrowser* browser) { furi_assert(browser); with_view_model( - browser->view, FileBrowserModel * model, { items_array_clear(model->items); }, false); + browser->view, + FileBrowserModel * model, + { + items_array_clear(model->items); + furi_string_free(model->current_filename); + }, + false); view_free(browser->view); + furi_timer_free(browser->timer); free(browser); } @@ -232,8 +236,10 @@ void file_browser_stop(FileBrowser* browser) { model->item_idx = 0; model->array_offset = 0; model->list_offset = 0; + furi_string_set(model->current_filename, ""); }, false); + furi_timer_stop(browser->timer); } void file_browser_set_callback(FileBrowser* browser, FileBrowserCallback callback, void* context) { @@ -336,6 +342,7 @@ static void model->is_root = is_root; model->list_loading = true; model->folder_loading = false; + furi_string_set(model->current_filename, file_browser_get_selected_filename(model)); }, true); browser_update_offset(browser); @@ -420,13 +427,7 @@ static void } } else { with_view_model( - browser->view, - FileBrowserModel * model, - { - items_array_sort(model->items); - model->list_loading = false; - }, - true); + browser->view, FileBrowserModel * model, { model->list_loading = false; }, true); } } @@ -492,8 +493,17 @@ static void browser_draw_list(Canvas* canvas, FileBrowserModel* model) { furi_string_set(filename, ". ."); } - elements_string_fit_width( - canvas, filename, (show_scrollbar ? MAX_LEN_PX - 6 : MAX_LEN_PX)); + uint8_t frame_width = (show_scrollbar ? MAX_LEN_PX - 6 : MAX_LEN_PX); + + bool is_long_filename = canvas_string_width(canvas, furi_string_get_cstr(filename)) > + frame_width; + + if(is_long_filename && model->item_idx == idx) { + furi_string_set(filename, model->current_filename); + elements_string_fit_width_trunc(canvas, filename, frame_width); + } else { + elements_string_fit_width(canvas, filename, frame_width); + } if(model->item_idx == idx) { browser_draw_frame(canvas, i, show_scrollbar); @@ -538,6 +548,36 @@ static void file_browser_view_draw_callback(Canvas* canvas, void* _model) { } } +static void file_browser_animate_filename_callback(void* context) { + furi_assert(context); + FileBrowser* browser = context; + with_view_model( + browser->view, + FileBrowserModel * model, + { + FuriString* old_filename; + old_filename = furi_string_alloc(); + furi_string_set(old_filename, model->current_filename); + furi_string_left(old_filename, 1); + furi_string_right(model->current_filename, 1); + furi_string_cat(model->current_filename, old_filename); + furi_string_free(old_filename); + }, + true); +} + +static const char* file_browser_get_selected_filename(FileBrowserModel* model) { + furi_assert(model); + BrowserItem_t* selected_item = NULL; + + if(browser_is_item_in_array(model, model->item_idx)) { + selected_item = items_array_get(model->items, model->item_idx - model->array_offset); + return furi_string_get_cstr(selected_item->display_name); + } + + return ""; +} + static bool file_browser_view_input_callback(InputEvent* event, void* context) { FileBrowser* browser = context; furi_assert(browser); @@ -558,6 +598,10 @@ static bool file_browser_view_input_callback(InputEvent* event, void* context) { if(event->key == InputKeyUp) { model->item_idx = ((model->item_idx - 1) + model->item_cnt) % model->item_cnt; + furi_string_set( + model->current_filename, file_browser_get_selected_filename(model)); + furi_string_cat(model->current_filename, " "); + furi_timer_start(browser->timer, 250); if(browser_is_list_load_required(model)) { model->list_loading = true; int32_t load_offset = CLAMP( @@ -569,6 +613,10 @@ static bool file_browser_view_input_callback(InputEvent* event, void* context) { } } else if(event->key == InputKeyDown) { model->item_idx = (model->item_idx + 1) % model->item_cnt; + furi_string_set( + model->current_filename, file_browser_get_selected_filename(model)); + furi_string_cat(model->current_filename, " "); + furi_timer_start(browser->timer, 250); if(browser_is_list_load_required(model)) { model->list_loading = true; int32_t load_offset = CLAMP( @@ -644,4 +692,4 @@ static bool file_browser_view_input_callback(InputEvent* event, void* context) { } return consumed; -} +} \ No newline at end of file diff --git a/applications/services/locale/application.fam b/applications/services/locale/application.fam new file mode 100644 index 000000000..d7912f642 --- /dev/null +++ b/applications/services/locale/application.fam @@ -0,0 +1,9 @@ +App( + appid="locale", + name="LocaleSrv", + apptype=FlipperAppType.STARTUP, + entry_point="locale_on_system_start", + cdefines=["SRV_LOCALE"], + order=90, + sdk_headers=["locale.h"], +) \ No newline at end of file diff --git a/applications/services/locale/locale.c b/applications/services/locale/locale.c new file mode 100644 index 000000000..805f25c3f --- /dev/null +++ b/applications/services/locale/locale.c @@ -0,0 +1,109 @@ +#include "locale.h" + +#define TAG "LocaleSrv" + +LocaleMeasurementUnit locale_get_measurement_unit(void) { + return furi_hal_rtc_get_locale_units(); +} + +void locale_set_measurement_unit(LocaleMeasurementUnit format) { + furi_hal_rtc_set_locale_units(format); +} + +LocaleTimeFormat locale_get_time_format(void) { + return furi_hal_rtc_get_locale_timeformat(); +} + +void locale_set_time_format(LocaleTimeFormat format) { + furi_hal_rtc_set_locale_timeformat(format); +} + +LocaleDateFormat locale_get_date_format(void) { + return furi_hal_rtc_get_locale_dateformat(); +} + +void locale_set_date_format(LocaleDateFormat format) { + furi_hal_rtc_set_locale_dateformat(format); +} + +float locale_fahrenheit_to_celsius(float temp_f) { + return (temp_f - 32.f) / 1.8f; +} + +float locale_celsius_to_fahrenheit(float temp_c) { + return (temp_c * 1.8f + 32.f); +} + +void locale_format_time( + FuriString* out_str, + FuriHalRtcDateTime* datetime, + LocaleTimeFormat format, + bool show_seconds) { + furi_assert(out_str); + furi_assert(datetime); + + uint8_t hours = datetime->hour; + uint8_t am_pm = 0; + if(format == LocaleTimeFormat12h) { + if(hours > 12) { + hours -= 12; + am_pm = 2; + } else { + am_pm = 1; + } + } + + if(show_seconds) { + furi_string_printf(out_str, "%02u:%02u:%02u", hours, datetime->minute, datetime->second); + } else { + furi_string_printf(out_str, "%02u:%02u", hours, datetime->minute); + } + + if(am_pm > 0) { + furi_string_cat_printf(out_str, " %s", (am_pm == 1) ? ("AM") : ("PM")); + } +} + +void locale_format_date( + FuriString* out_str, + FuriHalRtcDateTime* datetime, + LocaleDateFormat format, + char* separator) { + furi_assert(out_str); + furi_assert(datetime); + furi_assert(separator); + + if(format == LocaleDateFormatDMY) { + furi_string_printf( + out_str, + "%02u%s%02u%s%04u", + datetime->day, + separator, + datetime->month, + separator, + datetime->year); + } else if(format == LocaleDateFormatMDY) { + furi_string_printf( + out_str, + "%02u%s%02u%s%04u", + datetime->month, + separator, + datetime->day, + separator, + datetime->year); + } else { + furi_string_printf( + out_str, + "%04u%s%02u%s%02u", + datetime->year, + separator, + datetime->month, + separator, + datetime->day); + } +} + +int32_t locale_on_system_start(void* p) { + UNUSED(p); + return 0; +} \ No newline at end of file diff --git a/applications/services/locale/locale.h b/applications/services/locale/locale.h new file mode 100644 index 000000000..6491efbf6 --- /dev/null +++ b/applications/services/locale/locale.h @@ -0,0 +1,53 @@ +#pragma once + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + LocaleMeasurementUnitMetric = 0, /**< Meric mesurement units */ + LocaleMeasurementUnitImperial, /**< Imperial mesurement units */ +} LocaleMeasurementUnit; + +typedef enum { + LocaleTimeFormat24h = 0, /**< 24-hour format */ + LocaleTimeFormat12h, /**< 12-hour format */ +} LocaleTimeFormat; + +typedef enum { + LocaleDateFormatDMY = 0, /**< Day/Month/Year */ + LocaleDateFormatMDY, /**< Month/Day/Year */ + LocaleDateFormatYMD, /**< Year/Month/Day */ +} LocaleDateFormat; + +LocaleMeasurementUnit locale_get_measurement_unit(void); +void locale_set_measurement_unit(LocaleMeasurementUnit format); + +float locale_fahrenheit_to_celsius(float temp_f); +float locale_celsius_to_fahrenheit(float temp_c); + +LocaleTimeFormat locale_get_time_format(void); +void locale_set_time_format(LocaleTimeFormat format); + +void locale_format_time( + FuriString* out_str, + FuriHalRtcDateTime* datetime, + LocaleTimeFormat format, + bool show_seconds); + +LocaleDateFormat locale_get_date_format(void); +void locale_set_date_format(LocaleDateFormat format); + +void locale_format_date( + FuriString* out_str, + FuriHalRtcDateTime* datetime, + LocaleDateFormat format, + char* separator); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/applications/settings/system/application.fam b/applications/settings/system/application.fam index 0fc456b2f..69a8f1239 100644 --- a/applications/settings/system/application.fam +++ b/applications/settings/system/application.fam @@ -3,7 +3,7 @@ App( name="System", apptype=FlipperAppType.SETTINGS, entry_point="system_settings_app", - requires=["gui"], + requires=["gui", "locale"], stack_size=1 * 1024, order=70, ) diff --git a/applications/settings/system/system_settings.c b/applications/settings/system/system_settings.c index dfce11a22..b3e9c74c7 100644 --- a/applications/settings/system/system_settings.c +++ b/applications/settings/system/system_settings.c @@ -1,6 +1,7 @@ #include "system_settings.h" #include #include +#include const char* const log_level_text[] = { "Default", @@ -70,6 +71,59 @@ static void heap_trace_mode_changed(VariableItem* item) { furi_hal_rtc_set_heap_track_mode(heap_trace_mode_value[index]); } +const char* const mesurement_units_text[] = { + "Metric", + "Imperial", +}; + +const uint32_t mesurement_units_value[] = { + LocaleMeasurementUnitMetric, + LocaleMeasurementUnitImperial, +}; + +static void mesurement_units_changed(VariableItem* item) { + // SystemSettings* app = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + variable_item_set_current_value_text(item, mesurement_units_text[index]); + locale_set_measurement_unit(mesurement_units_value[index]); +} + +const char* const time_format_text[] = { + "24h", + "12h", +}; + +const uint32_t time_format_value[] = { + LocaleTimeFormat24h, + LocaleTimeFormat12h, +}; + +static void time_format_changed(VariableItem* item) { + // SystemSettings* app = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + variable_item_set_current_value_text(item, time_format_text[index]); + locale_set_time_format(time_format_value[index]); +} + +const char* const date_format_text[] = { + "D/M/Y", + "M/D/Y", + "Y/M/D", +}; + +const uint32_t date_format_value[] = { + LocaleDateFormatDMY, + LocaleDateFormatMDY, + LocaleDateFormatYMD, +}; + +static void date_format_changed(VariableItem* item) { + // SystemSettings* app = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + variable_item_set_current_value_text(item, date_format_text[index]); + locale_set_date_format(date_format_value[index]); +} + static uint32_t system_settings_exit(void* context) { UNUSED(context); return VIEW_NONE; @@ -91,6 +145,31 @@ SystemSettings* system_settings_alloc() { uint8_t value_index; app->var_item_list = variable_item_list_alloc(); + item = variable_item_list_add( + app->var_item_list, + "Units", + COUNT_OF(mesurement_units_text), + mesurement_units_changed, + app); + value_index = value_index_uint32( + locale_get_measurement_unit(), mesurement_units_value, COUNT_OF(mesurement_units_value)); + variable_item_set_current_value_index(item, value_index); + variable_item_set_current_value_text(item, mesurement_units_text[value_index]); + + item = variable_item_list_add( + app->var_item_list, "Time Format", COUNT_OF(time_format_text), time_format_changed, app); + value_index = value_index_uint32( + locale_get_time_format(), time_format_value, COUNT_OF(time_format_value)); + variable_item_set_current_value_index(item, value_index); + variable_item_set_current_value_text(item, time_format_text[value_index]); + + item = variable_item_list_add( + app->var_item_list, "Date Format", COUNT_OF(date_format_text), date_format_changed, app); + value_index = value_index_uint32( + locale_get_date_format(), date_format_value, COUNT_OF(date_format_value)); + variable_item_set_current_value_index(item, value_index); + variable_item_set_current_value_text(item, date_format_text[value_index]); + item = variable_item_list_add( app->var_item_list, "Log Level", COUNT_OF(log_level_text), log_level_changed, app); value_index = value_index_uint32( diff --git a/fbt_options.py b/fbt_options.py index 1552106be..4c3b39c62 100644 --- a/fbt_options.py +++ b/fbt_options.py @@ -14,7 +14,7 @@ DEBUG = 0 # Suffix to add to files when building distribution # If OS environment has DIST_SUFFIX set, it will be used instead -DIST_SUFFIX = "CC_CL-0011" +DIST_SUFFIX = "CC_CL-0012" # Coprocessor firmware COPRO_OB_DATA = "scripts/ob.data" diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index 4aaf7edc9..1fbda971d 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,11.0,, +Version,+,12.0,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -30,6 +30,7 @@ Header,+,applications/services/gui/view_dispatcher.h,, Header,+,applications/services/gui/view_stack.h,, Header,+,applications/services/input/input.h,, Header,+,applications/services/loader/loader.h,, +Header,+,applications/services/locale/locale.h,, Header,+,applications/services/notification/notification.h,, Header,+,applications/services/notification/notification_messages.h,, Header,+,applications/services/power/power_service/power.h,, @@ -784,6 +785,7 @@ Function,+,elements_scrollbar_pos,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint Function,+,elements_slightly_rounded_box,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t" Function,+,elements_slightly_rounded_frame,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t" Function,+,elements_string_fit_width,void,"Canvas*, FuriString*, uint8_t" +Function,+,elements_string_fit_width_trunc,void,"Canvas*, FuriString*, uint8_t" Function,+,elements_text_box,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t, Align, Align, const char*, _Bool" Function,+,empty_screen_alloc,EmptyScreen*, Function,+,empty_screen_free,void,EmptyScreen* @@ -1771,6 +1773,16 @@ Function,+,loader_update_menu,void, Function,+,loading_alloc,Loading*, Function,+,loading_free,void,Loading* Function,+,loading_get_view,View*,Loading* +Function,+,locale_celsius_to_fahrenheit,float,float +Function,+,locale_fahrenheit_to_celsius,float,float +Function,+,locale_format_date,void,"FuriString*, FuriHalRtcDateTime*, LocaleDateFormat, char*" +Function,+,locale_format_time,void,"FuriString*, FuriHalRtcDateTime*, LocaleTimeFormat, _Bool" +Function,+,locale_get_date_format,LocaleDateFormat, +Function,+,locale_get_measurement_unit,LocaleMeasurementUnit, +Function,+,locale_get_time_format,LocaleTimeFormat, +Function,+,locale_set_date_format,void,LocaleDateFormat +Function,+,locale_set_measurement_unit,void,LocaleMeasurementUnit +Function,+,locale_set_time_format,void,LocaleTimeFormat Function,-,localtime,tm*,const time_t* Function,-,localtime_r,tm*,"const time_t*, tm*" Function,-,log,double,double diff --git a/firmware/targets/f7/furi_hal/furi_hal_rtc.c b/firmware/targets/f7/furi_hal/furi_hal_rtc.c index c38cbfec5..818fb5d21 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_rtc.c +++ b/firmware/targets/f7/furi_hal/furi_hal_rtc.c @@ -31,10 +31,13 @@ typedef struct { uint8_t flags; uint8_t boot_mode : 4; uint8_t heap_track_mode : 2; - uint16_t reserved : 10; -} DeveloperReg; + uint8_t locale_units : 1; + uint8_t locale_timeformat : 1; + uint8_t locale_dateformat : 2; + uint8_t reserved : 6; +} SystemReg; -_Static_assert(sizeof(DeveloperReg) == 4, "DeveloperReg size mismatch"); +_Static_assert(sizeof(SystemReg) == 4, "SystemReg size mismatch"); #define FURI_HAL_RTC_SECONDS_PER_MINUTE 60 #define FURI_HAL_RTC_SECONDS_PER_HOUR (FURI_HAL_RTC_SECONDS_PER_MINUTE * 60) @@ -172,7 +175,7 @@ void furi_hal_rtc_set_register(FuriHalRtcRegister reg, uint32_t value) { void furi_hal_rtc_set_log_level(uint8_t level) { uint32_t data_reg = furi_hal_rtc_get_register(FuriHalRtcRegisterSystem); - DeveloperReg* data = (DeveloperReg*)&data_reg; + SystemReg* data = (SystemReg*)&data_reg; data->log_level = level; furi_hal_rtc_set_register(FuriHalRtcRegisterSystem, data_reg); furi_log_set_level(level); @@ -180,13 +183,13 @@ void furi_hal_rtc_set_log_level(uint8_t level) { uint8_t furi_hal_rtc_get_log_level() { uint32_t data_reg = furi_hal_rtc_get_register(FuriHalRtcRegisterSystem); - DeveloperReg* data = (DeveloperReg*)&data_reg; + SystemReg* data = (SystemReg*)&data_reg; return data->log_level; } void furi_hal_rtc_set_flag(FuriHalRtcFlag flag) { uint32_t data_reg = furi_hal_rtc_get_register(FuriHalRtcRegisterSystem); - DeveloperReg* data = (DeveloperReg*)&data_reg; + SystemReg* data = (SystemReg*)&data_reg; data->flags |= flag; furi_hal_rtc_set_register(FuriHalRtcRegisterSystem, data_reg); @@ -197,7 +200,7 @@ void furi_hal_rtc_set_flag(FuriHalRtcFlag flag) { void furi_hal_rtc_reset_flag(FuriHalRtcFlag flag) { uint32_t data_reg = furi_hal_rtc_get_register(FuriHalRtcRegisterSystem); - DeveloperReg* data = (DeveloperReg*)&data_reg; + SystemReg* data = (SystemReg*)&data_reg; data->flags &= ~flag; furi_hal_rtc_set_register(FuriHalRtcRegisterSystem, data_reg); @@ -208,36 +211,74 @@ void furi_hal_rtc_reset_flag(FuriHalRtcFlag flag) { bool furi_hal_rtc_is_flag_set(FuriHalRtcFlag flag) { uint32_t data_reg = furi_hal_rtc_get_register(FuriHalRtcRegisterSystem); - DeveloperReg* data = (DeveloperReg*)&data_reg; + SystemReg* data = (SystemReg*)&data_reg; return data->flags & flag; } void furi_hal_rtc_set_boot_mode(FuriHalRtcBootMode mode) { uint32_t data_reg = furi_hal_rtc_get_register(FuriHalRtcRegisterSystem); - DeveloperReg* data = (DeveloperReg*)&data_reg; + SystemReg* data = (SystemReg*)&data_reg; data->boot_mode = mode; furi_hal_rtc_set_register(FuriHalRtcRegisterSystem, data_reg); } FuriHalRtcBootMode furi_hal_rtc_get_boot_mode() { uint32_t data_reg = furi_hal_rtc_get_register(FuriHalRtcRegisterSystem); - DeveloperReg* data = (DeveloperReg*)&data_reg; + SystemReg* data = (SystemReg*)&data_reg; return (FuriHalRtcBootMode)data->boot_mode; } void furi_hal_rtc_set_heap_track_mode(FuriHalRtcHeapTrackMode mode) { uint32_t data_reg = furi_hal_rtc_get_register(FuriHalRtcRegisterSystem); - DeveloperReg* data = (DeveloperReg*)&data_reg; + SystemReg* data = (SystemReg*)&data_reg; data->heap_track_mode = mode; furi_hal_rtc_set_register(FuriHalRtcRegisterSystem, data_reg); } FuriHalRtcHeapTrackMode furi_hal_rtc_get_heap_track_mode() { uint32_t data_reg = furi_hal_rtc_get_register(FuriHalRtcRegisterSystem); - DeveloperReg* data = (DeveloperReg*)&data_reg; + SystemReg* data = (SystemReg*)&data_reg; return (FuriHalRtcHeapTrackMode)data->heap_track_mode; } +void furi_hal_rtc_set_locale_units(uint8_t mode) { + uint32_t data_reg = furi_hal_rtc_get_register(FuriHalRtcRegisterSystem); + SystemReg* data = (SystemReg*)&data_reg; + data->locale_units = mode; + furi_hal_rtc_set_register(FuriHalRtcRegisterSystem, data_reg); +} + +uint8_t furi_hal_rtc_get_locale_units() { + uint32_t data_reg = furi_hal_rtc_get_register(FuriHalRtcRegisterSystem); + SystemReg* data = (SystemReg*)&data_reg; + return (uint8_t)data->locale_units; +} + +void furi_hal_rtc_set_locale_timeformat(uint8_t mode) { + uint32_t data_reg = furi_hal_rtc_get_register(FuriHalRtcRegisterSystem); + SystemReg* data = (SystemReg*)&data_reg; + data->locale_timeformat = mode; + furi_hal_rtc_set_register(FuriHalRtcRegisterSystem, data_reg); +} + +uint8_t furi_hal_rtc_get_locale_timeformat() { + uint32_t data_reg = furi_hal_rtc_get_register(FuriHalRtcRegisterSystem); + SystemReg* data = (SystemReg*)&data_reg; + return (uint8_t)data->locale_timeformat; +} + +void furi_hal_rtc_set_locale_dateformat(uint8_t mode) { + uint32_t data_reg = furi_hal_rtc_get_register(FuriHalRtcRegisterSystem); + SystemReg* data = (SystemReg*)&data_reg; + data->locale_dateformat = mode; + furi_hal_rtc_set_register(FuriHalRtcRegisterSystem, data_reg); +} + +uint8_t furi_hal_rtc_get_locale_dateformat() { + uint32_t data_reg = furi_hal_rtc_get_register(FuriHalRtcRegisterSystem); + SystemReg* data = (SystemReg*)&data_reg; + return (uint8_t)data->locale_dateformat; +} void furi_hal_rtc_set_datetime(FuriHalRtcDateTime* datetime) { furi_assert(datetime); @@ -367,4 +408,4 @@ uint32_t furi_hal_rtc_datetime_to_timestamp(FuriHalRtcDateTime* datetime) { timestamp += datetime->second; return timestamp; -} +} \ No newline at end of file