diff --git a/applications/applications.c b/applications/applications.c index a4e81a88d..4e3b8087a 100644 --- a/applications/applications.c +++ b/applications/applications.c @@ -47,6 +47,7 @@ extern int32_t vibro_test_app(void* p); extern int32_t bt_hid_app(void* p); extern int32_t battery_test_app(void* p); extern int32_t text_box_test_app(void* p); +extern int32_t file_browser_app(void* p); // Plugins extern int32_t music_player_app(void* p); @@ -230,6 +231,22 @@ const FlipperApplication FLIPPER_APPS[] = { .flags = FlipperApplicationFlagDefault}, #endif +#ifdef APP_UNIVERSALRF + {.app = universal_rf_remote_app, + .name = "Universal SubGHz", + .stack_size = 2048, + .icon = &A_UniversalRemote_14, + .flags = FlipperApplicationFlagDefault}, +#endif + +#ifdef APP_JUKEBOX + {.app = jukebox_app, + .name = "Jukebox", + .stack_size = 2048, + .icon = &A_TouchTunes_14, + .flags = FlipperApplicationFlagDefault}, +#endif + #ifdef APP_LF_RFID {.app = lfrfid_app, .name = "125 kHz RFID", @@ -358,14 +375,6 @@ const FlipperApplication FLIPPER_PLUGINS[] = { }, #endif -#ifdef APP_JUKEBOX - {.app = jukebox_app, - .name = "Jukebox", - .stack_size = 2048, - .icon = &A_UniversalRemote_14, - .flags = FlipperApplicationFlagDefault}, -#endif - #ifdef APP_MUSIC_PLAYER {.app = music_player_app, .name = "Music Player", @@ -394,14 +403,6 @@ const FlipperApplication FLIPPER_PLUGINS[] = { {.app = tetris_game_app, .name = "Tetris Game", .stack_size = 1024, .icon = NULL}, #endif -#ifdef APP_UNIVERSALRF - {.app = universal_rf_remote_app, - .name = "Universal SubGHz", - .stack_size = 2048, - .icon = &A_UniversalRemote_14, - .flags = FlipperApplicationFlagDefault}, -#endif - {.app = wav_player_app, .name = "Wav Player", .stack_size = 4096, @@ -530,6 +531,14 @@ const FlipperApplication FLIPPER_DEBUG_APPS[] = { .flags = FlipperApplicationFlagDefault}, #endif +#ifdef APP_FILE_BROWSER_TEST + {.app = file_browser_app, + .name = "File Browser test", + .stack_size = 2048, + .icon = &A_BadUsb_14, + .flags = FlipperApplicationFlagDefault}, +#endif + #ifdef APP_BATTERY_TEST {.app = battery_test_app, .name = "Battery Test", diff --git a/applications/applications.mk b/applications/applications.mk index 1e5a22cbb..95f060df6 100644 --- a/applications/applications.mk +++ b/applications/applications.mk @@ -70,6 +70,7 @@ APP_USB_MOUSE = 1 APP_BAD_USB = 1 APP_U2F = 1 APP_UART_ECHO = 1 +APP_FILE_BROWSER_TEST = 1 endif @@ -228,6 +229,11 @@ CFLAGS += -DAPP_KEYPAD_TEST SRV_GUI = 1 endif +APP_FILE_BROWSER_TEST ?= 0 +ifeq ($(APP_FILE_BROWSER_TEST), 1) +CFLAGS += -DAPP_FILE_BROWSER_TEST +SRV_GUI = 1 +endif APP_ACCESSOR ?= 0 ifeq ($(APP_ACCESSOR), 1) diff --git a/applications/debug_tools/file_browser_test/file_browser_app.c b/applications/debug_tools/file_browser_test/file_browser_app.c new file mode 100644 index 000000000..a408f5cde --- /dev/null +++ b/applications/debug_tools/file_browser_test/file_browser_app.c @@ -0,0 +1,99 @@ +#include "assets_icons.h" +#include "file_browser_app_i.h" +#include "gui/modules/file_browser.h" +#include "m-string.h" +#include +#include +#include +#include + +static bool file_browser_app_custom_event_callback(void* context, uint32_t event) { + furi_assert(context); + FileBrowserApp* app = context; + return scene_manager_handle_custom_event(app->scene_manager, event); +} + +static bool file_browser_app_back_event_callback(void* context) { + furi_assert(context); + FileBrowserApp* app = context; + return scene_manager_handle_back_event(app->scene_manager); +} + +static void file_browser_app_tick_event_callback(void* context) { + furi_assert(context); + FileBrowserApp* app = context; + scene_manager_handle_tick_event(app->scene_manager); +} + +FileBrowserApp* file_browser_app_alloc(char* arg) { + UNUSED(arg); + FileBrowserApp* app = malloc(sizeof(FileBrowserApp)); + + app->gui = furi_record_open("gui"); + app->dialogs = furi_record_open("dialogs"); + + app->view_dispatcher = view_dispatcher_alloc(); + view_dispatcher_enable_queue(app->view_dispatcher); + + app->scene_manager = scene_manager_alloc(&file_browser_scene_handlers, app); + + view_dispatcher_set_event_callback_context(app->view_dispatcher, app); + view_dispatcher_set_tick_event_callback( + app->view_dispatcher, file_browser_app_tick_event_callback, 500); + view_dispatcher_set_custom_event_callback( + app->view_dispatcher, file_browser_app_custom_event_callback); + view_dispatcher_set_navigation_event_callback( + app->view_dispatcher, file_browser_app_back_event_callback); + + app->widget = widget_alloc(); + + string_init(app->file_path); + app->file_browser = file_browser_alloc(&(app->file_path)); + file_browser_configure(app->file_browser, "*", true, &I_badusb_10px, true); + + view_dispatcher_add_view( + app->view_dispatcher, FileBrowserAppViewStart, widget_get_view(app->widget)); + view_dispatcher_add_view( + app->view_dispatcher, FileBrowserAppViewResult, widget_get_view(app->widget)); + view_dispatcher_add_view( + app->view_dispatcher, FileBrowserAppViewBrowser, file_browser_get_view(app->file_browser)); + + view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); + + scene_manager_next_scene(app->scene_manager, FileBrowserSceneStart); + + return app; +} + +void file_browser_app_free(FileBrowserApp* app) { + furi_assert(app); + + // Views + view_dispatcher_remove_view(app->view_dispatcher, FileBrowserAppViewStart); + view_dispatcher_remove_view(app->view_dispatcher, FileBrowserAppViewResult); + view_dispatcher_remove_view(app->view_dispatcher, FileBrowserAppViewBrowser); + widget_free(app->widget); + file_browser_free(app->file_browser); + + // View dispatcher + view_dispatcher_free(app->view_dispatcher); + scene_manager_free(app->scene_manager); + + // Close records + furi_record_close("gui"); + furi_record_close("notification"); + furi_record_close("dialogs"); + + string_clear(app->file_path); + + free(app); +} + +int32_t file_browser_app(void* p) { + FileBrowserApp* file_browser_app = file_browser_app_alloc((char*)p); + + view_dispatcher_run(file_browser_app->view_dispatcher); + + file_browser_app_free(file_browser_app); + return 0; +} diff --git a/applications/debug_tools/file_browser_test/file_browser_app_i.h b/applications/debug_tools/file_browser_test/file_browser_app_i.h new file mode 100644 index 000000000..6e8412c9b --- /dev/null +++ b/applications/debug_tools/file_browser_test/file_browser_app_i.h @@ -0,0 +1,32 @@ +#pragma once + +#include "scenes/file_browser_scene.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +typedef struct FileBrowserApp FileBrowserApp; + +struct FileBrowserApp { + Gui* gui; + ViewDispatcher* view_dispatcher; + SceneManager* scene_manager; + DialogsApp* dialogs; + Widget* widget; + FileBrowser* file_browser; + + string_t file_path; +}; + +typedef enum { + FileBrowserAppViewStart, + FileBrowserAppViewBrowser, + FileBrowserAppViewResult, +} FileBrowserAppView; diff --git a/applications/debug_tools/file_browser_test/scenes/file_browser_scene.c b/applications/debug_tools/file_browser_test/scenes/file_browser_scene.c new file mode 100644 index 000000000..72a6e84d7 --- /dev/null +++ b/applications/debug_tools/file_browser_test/scenes/file_browser_scene.c @@ -0,0 +1,30 @@ +#include "file_browser_scene.h" + +// Generate scene on_enter handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter, +void (*const file_browser_scene_on_enter_handlers[])(void*) = { +#include "file_browser_scene_config.h" +}; +#undef ADD_SCENE + +// Generate scene on_event handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_event, +bool (*const file_browser_scene_on_event_handlers[])(void* context, SceneManagerEvent event) = { +#include "file_browser_scene_config.h" +}; +#undef ADD_SCENE + +// Generate scene on_exit handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_exit, +void (*const file_browser_scene_on_exit_handlers[])(void* context) = { +#include "file_browser_scene_config.h" +}; +#undef ADD_SCENE + +// Initialize scene handlers configuration structure +const SceneManagerHandlers file_browser_scene_handlers = { + .on_enter_handlers = file_browser_scene_on_enter_handlers, + .on_event_handlers = file_browser_scene_on_event_handlers, + .on_exit_handlers = file_browser_scene_on_exit_handlers, + .scene_num = FileBrowserSceneNum, +}; diff --git a/applications/debug_tools/file_browser_test/scenes/file_browser_scene.h b/applications/debug_tools/file_browser_test/scenes/file_browser_scene.h new file mode 100644 index 000000000..d690fca9f --- /dev/null +++ b/applications/debug_tools/file_browser_test/scenes/file_browser_scene.h @@ -0,0 +1,29 @@ +#pragma once + +#include + +// Generate scene id and total number +#define ADD_SCENE(prefix, name, id) FileBrowserScene##id, +typedef enum { +#include "file_browser_scene_config.h" + FileBrowserSceneNum, +} FileBrowserScene; +#undef ADD_SCENE + +extern const SceneManagerHandlers file_browser_scene_handlers; + +// Generate scene on_enter handlers declaration +#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*); +#include "file_browser_scene_config.h" +#undef ADD_SCENE + +// Generate scene on_event handlers declaration +#define ADD_SCENE(prefix, name, id) \ + bool prefix##_scene_##name##_on_event(void* context, SceneManagerEvent event); +#include "file_browser_scene_config.h" +#undef ADD_SCENE + +// Generate scene on_exit handlers declaration +#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_exit(void* context); +#include "file_browser_scene_config.h" +#undef ADD_SCENE diff --git a/applications/debug_tools/file_browser_test/scenes/file_browser_scene_browser.c b/applications/debug_tools/file_browser_test/scenes/file_browser_scene_browser.c new file mode 100644 index 000000000..9c570cec0 --- /dev/null +++ b/applications/debug_tools/file_browser_test/scenes/file_browser_scene_browser.c @@ -0,0 +1,45 @@ +#include "../file_browser_app_i.h" +#include "furi/check.h" +#include "furi/log.h" +#include "furi_hal.h" +#include "m-string.h" + +#define DEFAULT_PATH "/" +#define EXTENSION "*" + +bool file_browser_scene_browser_on_event(void* context, SceneManagerEvent event) { + UNUSED(context); + FileBrowserApp* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + scene_manager_next_scene(app->scene_manager, FileBrowserSceneResult); + consumed = true; + } else if(event.type == SceneManagerEventTypeTick) { + } + return consumed; +} + +static void file_browser_callback(void* context, bool state) { + FileBrowserApp* app = context; + furi_assert(app); + view_dispatcher_send_custom_event(app->view_dispatcher, SceneManagerEventTypeCustom); + + UNUSED(state); +} + +void file_browser_scene_browser_on_enter(void* context) { + FileBrowserApp* app = context; + + file_browser_set_callback(app->file_browser, file_browser_callback, app); + + file_browser_start(app->file_browser, app->file_path); + + view_dispatcher_switch_to_view(app->view_dispatcher, FileBrowserAppViewBrowser); +} + +void file_browser_scene_browser_on_exit(void* context) { + FileBrowserApp* app = context; + + file_browser_stop(app->file_browser); +} diff --git a/applications/debug_tools/file_browser_test/scenes/file_browser_scene_config.h b/applications/debug_tools/file_browser_test/scenes/file_browser_scene_config.h new file mode 100644 index 000000000..6597df3aa --- /dev/null +++ b/applications/debug_tools/file_browser_test/scenes/file_browser_scene_config.h @@ -0,0 +1,3 @@ +ADD_SCENE(file_browser, start, Start) +ADD_SCENE(file_browser, browser, Browser) +ADD_SCENE(file_browser, result, Result) diff --git a/applications/debug_tools/file_browser_test/scenes/file_browser_scene_result.c b/applications/debug_tools/file_browser_test/scenes/file_browser_scene_result.c new file mode 100644 index 000000000..53576cef4 --- /dev/null +++ b/applications/debug_tools/file_browser_test/scenes/file_browser_scene_result.c @@ -0,0 +1,36 @@ +#include "../file_browser_app_i.h" +#include "furi_hal.h" +#include "m-string.h" + +void file_browser_scene_result_ok_callback(InputType type, void* context) { + furi_assert(context); + FileBrowserApp* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, type); +} + +bool file_browser_scene_result_on_event(void* context, SceneManagerEvent event) { + UNUSED(context); + //FileBrowserApp* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + consumed = true; + } else if(event.type == SceneManagerEventTypeTick) { + } + return consumed; +} + +void file_browser_scene_result_on_enter(void* context) { + FileBrowserApp* app = context; + + widget_add_string_multiline_element( + app->widget, 64, 10, AlignCenter, AlignTop, FontSecondary, string_get_cstr(app->file_path)); + + view_dispatcher_switch_to_view(app->view_dispatcher, FileBrowserAppViewResult); +} + +void file_browser_scene_result_on_exit(void* context) { + UNUSED(context); + FileBrowserApp* app = context; + widget_reset(app->widget); +} diff --git a/applications/debug_tools/file_browser_test/scenes/file_browser_scene_start.c b/applications/debug_tools/file_browser_test/scenes/file_browser_scene_start.c new file mode 100644 index 000000000..bb71e83df --- /dev/null +++ b/applications/debug_tools/file_browser_test/scenes/file_browser_scene_start.c @@ -0,0 +1,44 @@ +#include "../file_browser_app_i.h" +#include "furi_hal.h" +#include "gui/modules/widget_elements/widget_element_i.h" + +static void + file_browser_scene_start_ok_callback(GuiButtonType result, InputType type, void* context) { + UNUSED(result); + furi_assert(context); + FileBrowserApp* app = context; + if(type == InputTypeShort) { + view_dispatcher_send_custom_event(app->view_dispatcher, type); + } +} + +bool file_browser_scene_start_on_event(void* context, SceneManagerEvent event) { + FileBrowserApp* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + string_set_str(app->file_path, "/any/badusb/demo_windows.txt"); + scene_manager_next_scene(app->scene_manager, FileBrowserSceneBrowser); + consumed = true; + } else if(event.type == SceneManagerEventTypeTick) { + } + return consumed; +} + +void file_browser_scene_start_on_enter(void* context) { + FileBrowserApp* app = context; + + widget_add_string_multiline_element( + app->widget, 64, 20, AlignCenter, AlignTop, FontSecondary, "Press OK to start"); + + widget_add_button_element( + app->widget, GuiButtonTypeCenter, "Ok", file_browser_scene_start_ok_callback, app); + + view_dispatcher_switch_to_view(app->view_dispatcher, FileBrowserAppViewStart); +} + +void file_browser_scene_start_on_exit(void* context) { + UNUSED(context); + FileBrowserApp* app = context; + widget_reset(app->widget); +} diff --git a/applications/desktop/desktop.c b/applications/desktop/desktop.c index 34c169e07..514166375 100644 --- a/applications/desktop/desktop.c +++ b/applications/desktop/desktop.c @@ -56,7 +56,12 @@ static bool desktop_custom_event_callback(void* context, uint32_t event) { return true; case DesktopGlobalAutoLock: if(!loader_is_locked(desktop->loader)) { - desktop_lock(desktop); + if(desktop->settings.pin_code.length > 0) { + desktop_pin_lock(&desktop->settings); + desktop_lock(desktop); + } else { + desktop_lock(desktop); + } } return true; } diff --git a/applications/gui/modules/file_browser.c b/applications/gui/modules/file_browser.c new file mode 100644 index 000000000..919750962 --- /dev/null +++ b/applications/gui/modules/file_browser.c @@ -0,0 +1,532 @@ +#include "file_browser.h" +#include "assets_icons.h" +#include "cmsis_os2.h" +#include "file_browser_worker.h" +#include "furi/check.h" +#include "furi/common_defines.h" +#include "furi/log.h" +#include "furi_hal_resources.h" +#include "m-string.h" +#include +#include +#include + +#define LIST_ITEMS 5u +#define MAX_LEN_PX 110 +#define FRAME_HEIGHT 12 +#define Y_OFFSET 3 + +#define ITEM_LIST_LEN_MAX 100 + +typedef enum { + BrowserItemTypeLoading, + BrowserItemTypeBack, + BrowserItemTypeFolder, + BrowserItemTypeFile, +} BrowserItemType; + +typedef struct { + string_t path; + BrowserItemType type; +} BrowserItem_t; + +static void BrowserItem_t_init(BrowserItem_t* obj) { + obj->type = BrowserItemTypeLoading; + string_init(obj->path); +} + +static void BrowserItem_t_init_set(BrowserItem_t* obj, const BrowserItem_t* src) { + obj->type = src->type; + string_init_set(obj->path, src->path); +} + +static void BrowserItem_t_set(BrowserItem_t* obj, const BrowserItem_t* src) { + obj->type = src->type; + string_set(obj->path, src->path); +} + +static void BrowserItem_t_clear(BrowserItem_t* obj) { + string_clear(obj->path); +} + +ARRAY_DEF( + 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; + BrowserWorker* worker; + char* ext_filter; + bool skip_assets; + + FileBrowserCallback callback; + void* context; + + string_t* result_path; +}; + +typedef struct { + items_array_t items; + + bool is_root; + bool folder_loading; + bool list_loading; + uint32_t item_cnt; + int32_t item_idx; + int32_t array_offset; + int32_t list_offset; + + const Icon* file_icon; + bool hide_ext; +} FileBrowserModel; + +static const Icon* BrowserItemIcons[] = { + [BrowserItemTypeLoading] = &I_loading_10px, + [BrowserItemTypeBack] = &I_back_10px, + [BrowserItemTypeFolder] = &I_dir_10px, + [BrowserItemTypeFile] = &I_unknown_10px, +}; + +static void file_browser_view_draw_callback(Canvas* canvas, void* _model); +static bool file_browser_view_input_callback(InputEvent* event, void* context); + +static void + browser_folder_open_cb(void* context, uint32_t item_cnt, int32_t file_idx, bool is_root); +static void browser_list_load_cb(void* context, uint32_t list_load_offset); +static void browser_list_item_cb(void* context, string_t item_path, bool is_folder, bool is_last); +static void browser_long_load_cb(void* context); + +FileBrowser* file_browser_alloc(string_t* result_path) { + furi_assert(result_path); + FileBrowser* browser = malloc(sizeof(FileBrowser)); + browser->view = view_alloc(); + view_allocate_model(browser->view, ViewModelTypeLocking, sizeof(FileBrowserModel)); + view_set_context(browser->view, browser); + view_set_draw_callback(browser->view, file_browser_view_draw_callback); + view_set_input_callback(browser->view, file_browser_view_input_callback); + + browser->result_path = result_path; + + with_view_model( + browser->view, (FileBrowserModel * model) { + items_array_init(model->items); + return false; + }); + + return browser; +} + +void file_browser_free(FileBrowser* browser) { + furi_assert(browser); + + with_view_model( + browser->view, (FileBrowserModel * model) { + items_array_clear(model->items); + return false; + }); + + view_free(browser->view); + free(browser); +} + +View* file_browser_get_view(FileBrowser* browser) { + furi_assert(browser); + return browser->view; +} + +void file_browser_configure( + FileBrowser* browser, + char* extension, + bool skip_assets, + const Icon* file_icon, + bool hide_ext) { + furi_assert(browser); + + browser->ext_filter = extension; + browser->skip_assets = skip_assets; + + with_view_model( + browser->view, (FileBrowserModel * model) { + model->file_icon = file_icon; + model->hide_ext = hide_ext; + return false; + }); +} + +void file_browser_start(FileBrowser* browser, string_t path) { + furi_assert(browser); + browser->worker = file_browser_worker_alloc(path, browser->ext_filter, browser->skip_assets); + file_browser_worker_set_callback_context(browser->worker, browser); + file_browser_worker_set_folder_callback(browser->worker, browser_folder_open_cb); + file_browser_worker_set_list_callback(browser->worker, browser_list_load_cb); + file_browser_worker_set_item_callback(browser->worker, browser_list_item_cb); + file_browser_worker_set_long_load_callback(browser->worker, browser_long_load_cb); +} + +void file_browser_stop(FileBrowser* browser) { + furi_assert(browser); + file_browser_worker_free(browser->worker); + with_view_model( + browser->view, (FileBrowserModel * model) { + items_array_reset(model->items); + model->item_cnt = 0; + model->item_idx = 0; + model->array_offset = 0; + model->list_offset = 0; + return false; + }); +} + +void file_browser_set_callback(FileBrowser* browser, FileBrowserCallback callback, void* context) { + browser->context = context; + browser->callback = callback; +} + +static bool browser_is_item_in_array(FileBrowserModel* model, uint32_t idx) { + size_t array_size = items_array_size(model->items); + + if((idx >= (uint32_t)model->array_offset + array_size) || + (idx < (uint32_t)model->array_offset)) { + return false; + } + return true; +} + +static bool browser_is_list_load_required(FileBrowserModel* model) { + size_t array_size = items_array_size(model->items); + uint32_t item_cnt = (model->is_root) ? model->item_cnt : model->item_cnt - 1; + + if((model->list_loading) || (array_size >= item_cnt)) { + return false; + } + + if((model->array_offset > 0) && + (model->item_idx < (model->array_offset + ITEM_LIST_LEN_MAX / 4))) { + return true; + } + + if(((model->array_offset + array_size) < item_cnt) && + (model->item_idx > (int32_t)(model->array_offset + array_size - ITEM_LIST_LEN_MAX / 4))) { + return true; + } + + return false; +} + +static void browser_update_offset(FileBrowser* browser) { + furi_assert(browser); + + with_view_model( + browser->view, (FileBrowserModel * model) { + uint16_t bounds = model->item_cnt > (LIST_ITEMS - 1) ? 2 : model->item_cnt; + + if((model->item_cnt > (LIST_ITEMS - 1)) && + (model->item_idx >= ((int32_t)model->item_cnt - 1))) { + model->list_offset = model->item_idx - (LIST_ITEMS - 1); + } else if(model->list_offset < model->item_idx - bounds) { + model->list_offset = CLAMP( + model->item_idx - (int32_t)(LIST_ITEMS - 2), + (int32_t)model->item_cnt - bounds, + 0); + } else if(model->list_offset > model->item_idx - bounds) { + model->list_offset = + CLAMP(model->item_idx - 1, (int32_t)model->item_cnt - bounds, 0); + } + + return false; + }); +} + +static void + browser_folder_open_cb(void* context, uint32_t item_cnt, int32_t file_idx, bool is_root) { + furi_assert(context); + FileBrowser* browser = (FileBrowser*)context; + + int32_t load_offset = 0; + + with_view_model( + browser->view, (FileBrowserModel * model) { + if(is_root) { + model->item_cnt = item_cnt; + model->item_idx = (file_idx > 0) ? file_idx : 0; + load_offset = + CLAMP(model->item_idx - ITEM_LIST_LEN_MAX / 2, (int32_t)model->item_cnt, 0); + } else { + model->item_cnt = item_cnt + 1; + model->item_idx = file_idx + 1; + load_offset = CLAMP( + model->item_idx - ITEM_LIST_LEN_MAX / 2 - 1, (int32_t)model->item_cnt - 1, 0); + } + model->array_offset = 0; + model->list_offset = 0; + model->is_root = is_root; + model->list_loading = true; + model->folder_loading = false; + return true; + }); + browser_update_offset(browser); + + file_browser_worker_load(browser->worker, load_offset, ITEM_LIST_LEN_MAX); +} + +static void browser_list_load_cb(void* context, uint32_t list_load_offset) { + furi_assert(context); + FileBrowser* browser = (FileBrowser*)context; + + BrowserItem_t back_item; + BrowserItem_t_init(&back_item); + back_item.type = BrowserItemTypeBack; + + with_view_model( + browser->view, (FileBrowserModel * model) { + items_array_reset(model->items); + model->array_offset = list_load_offset; + if(!model->is_root) { + if(list_load_offset == 0) { + items_array_push_back(model->items, back_item); + } else { + model->array_offset += 1; + } + } + return false; + }); + + BrowserItem_t_clear(&back_item); +} + +static void browser_list_item_cb(void* context, string_t item_path, bool is_folder, bool is_last) { + furi_assert(context); + FileBrowser* browser = (FileBrowser*)context; + + BrowserItem_t item; + + if(!is_last) { + BrowserItem_t_init(&item); + string_set(item.path, item_path); + item.type = (is_folder) ? BrowserItemTypeFolder : BrowserItemTypeFile; + + with_view_model( + browser->view, (FileBrowserModel * model) { + items_array_push_back(model->items, item); + return false; + }); + BrowserItem_t_clear(&item); + } else { + with_view_model( + browser->view, (FileBrowserModel * model) { + model->list_loading = false; + return true; + }); + } +} + +static void browser_long_load_cb(void* context) { + furi_assert(context); + FileBrowser* browser = (FileBrowser*)context; + + with_view_model( + browser->view, (FileBrowserModel * model) { + model->folder_loading = true; + return true; + }); +} + +static void browser_draw_frame(Canvas* canvas, uint16_t idx, bool scrollbar) { + canvas_set_color(canvas, ColorBlack); + canvas_draw_box( + canvas, 0, Y_OFFSET + idx * FRAME_HEIGHT, (scrollbar ? 122 : 127), FRAME_HEIGHT); + + canvas_set_color(canvas, ColorWhite); + canvas_draw_dot(canvas, 0, Y_OFFSET + idx * FRAME_HEIGHT); + canvas_draw_dot(canvas, 1, Y_OFFSET + idx * FRAME_HEIGHT); + canvas_draw_dot(canvas, 0, (Y_OFFSET + idx * FRAME_HEIGHT) + 1); + + canvas_draw_dot(canvas, 0, (Y_OFFSET + idx * FRAME_HEIGHT) + (FRAME_HEIGHT - 1)); + canvas_draw_dot(canvas, scrollbar ? 121 : 126, Y_OFFSET + idx * FRAME_HEIGHT); + canvas_draw_dot( + canvas, scrollbar ? 121 : 126, (Y_OFFSET + idx * FRAME_HEIGHT) + (FRAME_HEIGHT - 1)); +} + +static void browser_draw_loading(Canvas* canvas, FileBrowserModel* model) { + uint8_t width = 49; + uint8_t height = 47; + uint8_t x = 128 / 2 - width / 2; + uint8_t y = 64 / 2 - height / 2; + + UNUSED(model); + + elements_bold_rounded_frame(canvas, x, y, width, height); + + canvas_set_font(canvas, FontSecondary); + elements_multiline_text(canvas, x + 7, y + 13, "Loading..."); + + canvas_draw_icon(canvas, x + 13, y + 19, &A_Loading_24); +} + +static void browser_draw_list(Canvas* canvas, FileBrowserModel* model) { + uint32_t array_size = items_array_size(model->items); + bool show_scrollbar = model->item_cnt > LIST_ITEMS; + + string_t filename; + string_init(filename); + + for(uint32_t i = 0; i < MIN(model->item_cnt, LIST_ITEMS); i++) { + int32_t idx = CLAMP((uint32_t)(i + model->list_offset), model->item_cnt, 0u); + + BrowserItemType item_type = BrowserItemTypeLoading; + + if(browser_is_item_in_array(model, idx)) { + BrowserItem_t* item = items_array_get( + model->items, CLAMP(idx - model->array_offset, (int32_t)(array_size - 1), 0)); + item_type = item->type; + file_browser_worker_get_filename( + item->path, filename, (model->hide_ext) && (item_type == BrowserItemTypeFile)); + } else { + string_set_str(filename, "---"); + } + + if(item_type == BrowserItemTypeBack) { + string_set_str(filename, ". ."); + } + + elements_string_fit_width( + canvas, filename, (show_scrollbar ? MAX_LEN_PX - 6 : MAX_LEN_PX)); + + if(model->item_idx == idx) { + browser_draw_frame(canvas, i, show_scrollbar); + } else { + canvas_set_color(canvas, ColorBlack); + } + + if((item_type == BrowserItemTypeFile) && (model->file_icon)) { + canvas_draw_icon(canvas, 2, Y_OFFSET + 1 + i * FRAME_HEIGHT, model->file_icon); + } else if(BrowserItemIcons[item_type] != NULL) { + canvas_draw_icon( + canvas, 2, Y_OFFSET + 1 + i * FRAME_HEIGHT, BrowserItemIcons[item_type]); + } + canvas_draw_str(canvas, 15, Y_OFFSET + 9 + i * FRAME_HEIGHT, string_get_cstr(filename)); + } + + if(show_scrollbar) { + elements_scrollbar_pos( + canvas, + 126, + Y_OFFSET, + canvas_height(canvas) - Y_OFFSET, + model->item_idx, + model->item_cnt); + } + + string_clear(filename); +} + +static void file_browser_view_draw_callback(Canvas* canvas, void* _model) { + FileBrowserModel* model = _model; + + if(model->folder_loading) { + browser_draw_loading(canvas, model); + } else { + browser_draw_list(canvas, model); + } +} + +static bool file_browser_view_input_callback(InputEvent* event, void* context) { + FileBrowser* browser = context; + furi_assert(browser); + bool consumed = false; + bool is_loading = false; + + with_view_model( + browser->view, (FileBrowserModel * model) { + is_loading = model->folder_loading; + return false; + }); + + if(is_loading) { + return false; + } else if(event->key == InputKeyUp || event->key == InputKeyDown) { + if(event->type == InputTypeShort || event->type == InputTypeRepeat) { + with_view_model( + browser->view, (FileBrowserModel * model) { + if(event->key == InputKeyUp) { + model->item_idx = + ((model->item_idx - 1) + model->item_cnt) % model->item_cnt; + if(browser_is_list_load_required(model)) { + model->list_loading = true; + int32_t load_offset = CLAMP( + model->item_idx - ITEM_LIST_LEN_MAX / 4 * 3, + (int32_t)model->item_cnt - ITEM_LIST_LEN_MAX, + 0); + file_browser_worker_load( + browser->worker, load_offset, ITEM_LIST_LEN_MAX); + } + } else if(event->key == InputKeyDown) { + model->item_idx = (model->item_idx + 1) % model->item_cnt; + if(browser_is_list_load_required(model)) { + model->list_loading = true; + int32_t load_offset = CLAMP( + model->item_idx - ITEM_LIST_LEN_MAX / 4 * 1, + (int32_t)model->item_cnt - ITEM_LIST_LEN_MAX, + 0); + file_browser_worker_load( + browser->worker, load_offset, ITEM_LIST_LEN_MAX); + } + } + return true; + }); + browser_update_offset(browser); + consumed = true; + } + } else if(event->key == InputKeyOk) { + if(event->type == InputTypeShort) { + BrowserItem_t* selected_item = NULL; + int32_t select_index = 0; + with_view_model( + browser->view, (FileBrowserModel * model) { + if(browser_is_item_in_array(model, model->item_idx)) { + selected_item = + items_array_get(model->items, model->item_idx - model->array_offset); + select_index = model->item_idx; + if((!model->is_root) && (select_index > 0)) { + select_index -= 1; + } + } + return false; + }); + + if(selected_item) { + if(selected_item->type == BrowserItemTypeBack) { + file_browser_worker_folder_exit(browser->worker); + } else if(selected_item->type == BrowserItemTypeFolder) { + file_browser_worker_folder_enter( + browser->worker, selected_item->path, select_index); + } else if(selected_item->type == BrowserItemTypeFile) { + string_set(*(browser->result_path), selected_item->path); + if(browser->callback) { + browser->callback(browser->context, true); + } + } + } + consumed = true; + } + } else if(event->key == InputKeyLeft) { + if(event->type == InputTypeShort) { + bool is_root = false; + with_view_model( + browser->view, (FileBrowserModel * model) { + is_root = model->is_root; + return false; + }); + if(!is_root) { + file_browser_worker_folder_exit(browser->worker); + } + consumed = true; + } + } + + return consumed; +} diff --git a/applications/gui/modules/file_browser.h b/applications/gui/modules/file_browser.h new file mode 100644 index 000000000..b77c6e65c --- /dev/null +++ b/applications/gui/modules/file_browser.h @@ -0,0 +1,39 @@ +/** + * @file file_browser.h + * GUI: FileBrowser view module API + */ + +#pragma once + +#include "m-string.h" +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct FileBrowser FileBrowser; +typedef void (*FileBrowserCallback)(void* context, bool state); + +FileBrowser* file_browser_alloc(string_t* result_path); + +void file_browser_free(FileBrowser* browser); + +View* file_browser_get_view(FileBrowser* browser); + +void file_browser_configure( + FileBrowser* browser, + char* extension, + bool skip_assets, + const Icon* file_icon, + bool hide_ext); + +void file_browser_start(FileBrowser* browser, string_t path); + +void file_browser_stop(FileBrowser* browser); + +void file_browser_set_callback(FileBrowser* browser, FileBrowserCallback callback, void* context); + +#ifdef __cplusplus +} +#endif diff --git a/applications/gui/modules/file_browser_worker.c b/applications/gui/modules/file_browser_worker.c new file mode 100644 index 000000000..13fc97111 --- /dev/null +++ b/applications/gui/modules/file_browser_worker.c @@ -0,0 +1,420 @@ +#include "file_browser_worker.h" +#include "furi/check.h" +#include "furi/common_defines.h" +#include "m-string.h" +#include "storage/filesystem_api_defines.h" +#include +#include +#include +#include +#include + +#define TAG "BrowserWorker" + +#define ASSETS_DIR "assets" +#define BROWSER_ROOT "/any" +#define FILE_NAME_LEN_MAX 256 +#define LONG_LOAD_THRESHOLD 100 + +typedef enum { + WorkerEvtStop = (1 << 0), + WorkerEvtLoad = (1 << 1), + WorkerEvtFolderEnter = (1 << 2), + WorkerEvtFolderExit = (1 << 3), +} WorkerEvtFlags; + +#define WORKER_FLAGS_ALL \ + (WorkerEvtStop | WorkerEvtLoad | WorkerEvtFolderEnter | WorkerEvtFolderExit) + +ARRAY_DEF(idx_last_array, int32_t) + +struct BrowserWorker { + FuriThread* thread; + + string_t filter_extension; + string_t path_next; + int32_t item_sel_idx; + uint32_t load_offset; + uint32_t load_count; + bool skip_assets; + idx_last_array_t idx_last; + + void* cb_ctx; + BrowserWorkerFolderOpenCallback folder_cb; + BrowserWorkerListLoadCallback list_load_cb; + BrowserWorkerListItemCallback list_item_cb; + BrowserWorkerLongLoadCallback long_load_cb; +}; + +static bool browser_path_is_file(string_t path) { + bool state = false; + FileInfo file_info; + Storage* storage = furi_record_open("storage"); + if(storage_common_stat(storage, string_get_cstr(path), &file_info) == FSE_OK) { + if((file_info.flags & FSF_DIRECTORY) == 0) { + state = true; + } + } + furi_record_close("storage"); + return state; +} + +static bool browser_path_trim(string_t path) { + bool is_root = false; + size_t filename_start = string_search_rchar(path, '/'); + string_left(path, filename_start); + if((string_empty_p(path)) || (filename_start == STRING_FAILURE)) { + string_set_str(path, BROWSER_ROOT); + is_root = true; + } + return is_root; +} + +static bool browser_filter_by_name(BrowserWorker* browser, string_t name, bool is_folder) { + if(is_folder) { + // Skip assets folders (if enabled) + if(browser->skip_assets) { + return ((string_cmp_str(name, ASSETS_DIR) == 0) ? (false) : (true)); + } else { + return true; + } + } else { + // Filter files by extension + if((string_empty_p(browser->filter_extension)) || + (string_cmp_str(browser->filter_extension, "*") == 0)) { + return true; + } + if(string_end_with_string_p(name, browser->filter_extension)) { + return true; + } + } + return false; +} + +static bool browser_folder_check_and_switch(string_t path) { + FileInfo file_info; + Storage* storage = furi_record_open("storage"); + bool is_root = false; + while(1) { + // Check if folder is existing and navigate back if not + if(storage_common_stat(storage, string_get_cstr(path), &file_info) == FSE_OK) { + if(file_info.flags & FSF_DIRECTORY) { + break; + } + } + if(is_root) { + break; + } + is_root = browser_path_trim(path); + } + furi_record_close("storage"); + return is_root; +} + +static bool browser_folder_init( + BrowserWorker* browser, + string_t path, + string_t filename, + uint32_t* item_cnt, + int32_t* file_idx) { + bool state = false; + FileInfo file_info; + uint32_t total_files_cnt = 0; + + Storage* storage = furi_record_open("storage"); + File* directory = storage_file_alloc(storage); + + char name_temp[FILE_NAME_LEN_MAX]; + string_t name_str; + string_init(name_str); + + *item_cnt = 0; + *file_idx = -1; + + if(storage_dir_open(directory, string_get_cstr(path))) { + state = true; + while(1) { + if(!storage_dir_read(directory, &file_info, name_temp, FILE_NAME_LEN_MAX)) { + break; + } + if((storage_file_get_error(directory) == FSE_OK) && (name_temp[0] != '\0')) { + total_files_cnt++; + string_set_str(name_str, name_temp); + if(browser_filter_by_name(browser, name_str, (file_info.flags & FSF_DIRECTORY))) { + if(!string_empty_p(filename)) { + if(string_cmp(name_str, filename) == 0) { + *file_idx = *item_cnt; + } + } + (*item_cnt)++; + } + if(total_files_cnt == LONG_LOAD_THRESHOLD) { + if(browser->long_load_cb) { + browser->long_load_cb(browser->cb_ctx); + } + } + } + } + } + + string_clear(name_str); + + storage_dir_close(directory); + storage_file_free(directory); + + furi_record_close("storage"); + + return state; +} + +static bool + browser_folder_load(BrowserWorker* browser, string_t path, uint32_t offset, uint32_t count) { + FileInfo file_info; + + Storage* storage = furi_record_open("storage"); + File* directory = storage_file_alloc(storage); + + char name_temp[FILE_NAME_LEN_MAX]; + string_t name_str; + string_init(name_str); + + uint32_t items_cnt = 0; + + do { + if(!storage_dir_open(directory, string_get_cstr(path))) { + break; + } + + items_cnt = 0; + while(items_cnt < offset) { + if(!storage_dir_read(directory, &file_info, name_temp, FILE_NAME_LEN_MAX)) { + break; + } + if(storage_file_get_error(directory) == FSE_OK) { + string_set_str(name_str, name_temp); + if(browser_filter_by_name(browser, name_str, (file_info.flags & FSF_DIRECTORY))) { + items_cnt++; + } + } else { + break; + } + } + if(items_cnt != offset) { + break; + } + + if(browser->list_load_cb) { + browser->list_load_cb(browser->cb_ctx, offset); + } + + items_cnt = 0; + while(items_cnt < count) { + if(!storage_dir_read(directory, &file_info, name_temp, FILE_NAME_LEN_MAX)) { + break; + } + if(storage_file_get_error(directory) == FSE_OK) { + string_set_str(name_str, name_temp); + if(browser_filter_by_name(browser, name_str, (file_info.flags & FSF_DIRECTORY))) { + string_printf(name_str, "%s/%s", string_get_cstr(path), name_temp); + if(browser->list_item_cb) { + browser->list_item_cb( + browser->cb_ctx, name_str, (file_info.flags & FSF_DIRECTORY), false); + } + items_cnt++; + } + } else { + break; + } + } + if(browser->list_item_cb) { + browser->list_item_cb(browser->cb_ctx, NULL, false, true); + } + } while(0); + + string_clear(name_str); + + storage_dir_close(directory); + storage_file_free(directory); + + furi_record_close("storage"); + + return (items_cnt == count); +} + +static int32_t browser_worker(void* context) { + BrowserWorker* browser = (BrowserWorker*)context; + furi_assert(browser); + FURI_LOG_D(TAG, "Start"); + + uint32_t items_cnt = 0; + string_t path; + string_init_set_str(path, BROWSER_ROOT); + browser->item_sel_idx = -1; + + // If start path is a path to the file - try finding index of this file in a folder + string_t filename; + string_init(filename); + if(browser_path_is_file(browser->path_next)) { + file_browser_worker_get_filename(browser->path_next, filename, false); + } + + osThreadFlagsSet(furi_thread_get_thread_id(browser->thread), WorkerEvtFolderEnter); + + while(1) { + uint32_t flags = osThreadFlagsWait(WORKER_FLAGS_ALL, osFlagsWaitAny, osWaitForever); + furi_assert((flags & osFlagsError) == 0); + + if(flags & WorkerEvtFolderEnter) { + string_set(path, browser->path_next); + bool is_root = browser_folder_check_and_switch(path); + + // Push previous selected item index to history array + idx_last_array_push_back(browser->idx_last, browser->item_sel_idx); + + int32_t file_idx = 0; + browser_folder_init(browser, path, filename, &items_cnt, &file_idx); + FURI_LOG_D( + TAG, + "Enter folder: %s items: %u idx: %d", + string_get_cstr(path), + items_cnt, + file_idx); + if(browser->folder_cb) { + browser->folder_cb(browser->cb_ctx, items_cnt, file_idx, is_root); + } + string_reset(filename); + } + + if(flags & WorkerEvtFolderExit) { + browser_path_trim(path); + bool is_root = browser_folder_check_and_switch(path); + + int32_t file_idx = 0; + browser_folder_init(browser, path, filename, &items_cnt, &file_idx); + if(idx_last_array_size(browser->idx_last) > 0) { + // Pop previous selected item index from history array + idx_last_array_pop_back(&file_idx, browser->idx_last); + } + FURI_LOG_D( + TAG, "Exit to: %s items: %u idx: %d", string_get_cstr(path), items_cnt, file_idx); + if(browser->folder_cb) { + browser->folder_cb(browser->cb_ctx, items_cnt, file_idx, is_root); + } + } + + if(flags & WorkerEvtLoad) { + FURI_LOG_D(TAG, "Load offset: %u cnt: %u", browser->load_offset, browser->load_count); + browser_folder_load(browser, path, browser->load_offset, browser->load_count); + } + + if(flags & WorkerEvtStop) { + break; + } + } + + string_clear(filename); + string_clear(path); + + FURI_LOG_D(TAG, "End"); + return 0; +} + +void file_browser_worker_get_filename(string_t path, string_t name, bool trim_ext) { + size_t filename_start = string_search_rchar(path, '/'); + if(filename_start > 0) { + filename_start++; + string_set_n(name, path, filename_start, string_size(path) - filename_start); + } + if(trim_ext) { + size_t dot = string_search_rchar(name, '.'); + if(dot > 0) { + string_left(name, dot); + } + } +} + +BrowserWorker* file_browser_worker_alloc(string_t path, char* filter_ext, bool skip_assets) { + BrowserWorker* browser = malloc(sizeof(BrowserWorker)); + + idx_last_array_init(browser->idx_last); + + string_init_set_str(browser->filter_extension, filter_ext); + browser->skip_assets = skip_assets; + string_init_set(browser->path_next, path); + + browser->thread = furi_thread_alloc(); + furi_thread_set_name(browser->thread, "BrowserWorker"); + furi_thread_set_stack_size(browser->thread, 2048); + furi_thread_set_context(browser->thread, browser); + furi_thread_set_callback(browser->thread, browser_worker); + furi_thread_start(browser->thread); + + return browser; +} + +void file_browser_worker_free(BrowserWorker* browser) { + furi_assert(browser); + + osThreadFlagsSet(furi_thread_get_thread_id(browser->thread), WorkerEvtStop); + furi_thread_join(browser->thread); + furi_thread_free(browser->thread); + + string_clear(browser->filter_extension); + string_clear(browser->path_next); + + idx_last_array_clear(browser->idx_last); + + free(browser); +} + +void file_browser_worker_set_callback_context(BrowserWorker* browser, void* context) { + furi_assert(browser); + browser->cb_ctx = context; +} + +void file_browser_worker_set_folder_callback( + BrowserWorker* browser, + BrowserWorkerFolderOpenCallback cb) { + furi_assert(browser); + browser->folder_cb = cb; +} + +void file_browser_worker_set_list_callback( + BrowserWorker* browser, + BrowserWorkerListLoadCallback cb) { + furi_assert(browser); + browser->list_load_cb = cb; +} + +void file_browser_worker_set_item_callback( + BrowserWorker* browser, + BrowserWorkerListItemCallback cb) { + furi_assert(browser); + browser->list_item_cb = cb; +} + +void file_browser_worker_set_long_load_callback( + BrowserWorker* browser, + BrowserWorkerLongLoadCallback cb) { + furi_assert(browser); + browser->long_load_cb = cb; +} + +void file_browser_worker_folder_enter(BrowserWorker* browser, string_t path, int32_t item_idx) { + furi_assert(browser); + string_set(browser->path_next, path); + browser->item_sel_idx = item_idx; + osThreadFlagsSet(furi_thread_get_thread_id(browser->thread), WorkerEvtFolderEnter); +} + +void file_browser_worker_folder_exit(BrowserWorker* browser) { + furi_assert(browser); + osThreadFlagsSet(furi_thread_get_thread_id(browser->thread), WorkerEvtFolderExit); +} + +void file_browser_worker_load(BrowserWorker* browser, uint32_t offset, uint32_t count) { + furi_assert(browser); + browser->load_offset = offset; + browser->load_count = count; + osThreadFlagsSet(furi_thread_get_thread_id(browser->thread), WorkerEvtLoad); +} diff --git a/applications/gui/modules/file_browser_worker.h b/applications/gui/modules/file_browser_worker.h new file mode 100644 index 000000000..821d5103f --- /dev/null +++ b/applications/gui/modules/file_browser_worker.h @@ -0,0 +1,57 @@ +#pragma once + +#include "m-string.h" +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct BrowserWorker BrowserWorker; +typedef void (*BrowserWorkerFolderOpenCallback)( + void* context, + uint32_t item_cnt, + int32_t file_idx, + bool is_root); +typedef void (*BrowserWorkerListLoadCallback)(void* context, uint32_t list_load_offset); +typedef void (*BrowserWorkerListItemCallback)( + void* context, + string_t item_path, + bool is_folder, + bool is_last); +typedef void (*BrowserWorkerLongLoadCallback)(void* context); + +void file_browser_worker_get_filename(string_t path, string_t name, bool trim_ext); + +BrowserWorker* file_browser_worker_alloc(string_t path, char* filter_ext, bool skip_assets); + +void file_browser_worker_free(BrowserWorker* browser); + +void file_browser_worker_set_callback_context(BrowserWorker* browser, void* context); + +void file_browser_worker_set_folder_callback( + BrowserWorker* browser, + BrowserWorkerFolderOpenCallback cb); + +void file_browser_worker_set_list_callback( + BrowserWorker* browser, + BrowserWorkerListLoadCallback cb); + +void file_browser_worker_set_item_callback( + BrowserWorker* browser, + BrowserWorkerListItemCallback cb); + +void file_browser_worker_set_long_load_callback( + BrowserWorker* browser, + BrowserWorkerLongLoadCallback cb); + +void file_browser_worker_folder_enter(BrowserWorker* browser, string_t path, int32_t item_idx); + +void file_browser_worker_folder_exit(BrowserWorker* browser); + +void file_browser_worker_load(BrowserWorker* browser, uint32_t offset, uint32_t count); + +#ifdef __cplusplus +} +#endif diff --git a/applications/jukebox/jukebox.c b/applications/jukebox/jukebox.c index 17bfa89d6..d4eb814d5 100644 --- a/applications/jukebox/jukebox.c +++ b/applications/jukebox/jukebox.c @@ -7,6 +7,7 @@ #include #include #include +#include #define TAG "JukeBox" @@ -31,7 +32,7 @@ static char* subString(char* someString, int n) { char* new = malloc(sizeof(char) * n + 1); strncpy(new, someString, n); new[n] = '\0'; - return new; + return(new); } static char* file_stub(const char* file_name) { @@ -40,7 +41,7 @@ static char* file_stub(const char* file_name) { // string_init(file_name); path_extract_filename_no_ext(file_name, filename); - return subString((char*)string_get_cstr(filename), 8); + return(subString((char*)string_get_cstr(filename), 8)); } static void jukebox_send_signal(uint32_t frequency, string_t signal, string_t protocol) { @@ -58,7 +59,7 @@ static void jukebox_send_signal(uint32_t frequency, string_t signal, string_t pr } else { return; } - + NotificationApp* notification = furi_record_open("notification"); FlipperFormat* flipper_format = flipper_format_string_alloc(); Stream* stream = flipper_format_get_raw_stream(flipper_format); stream_clean(stream); @@ -77,6 +78,8 @@ static void jukebox_send_signal(uint32_t frequency, string_t signal, string_t pr TAG, "Transmitting at %lu, repeat %lu. Press CTRL+C to stop\r\n", frequency, repeat); furi_hal_power_suppress_charge_enter(); + notification_message(notification, &sequence_set_vibro_on); + furi_hal_subghz_start_async_tx(subghz_transmitter_yield, transmitter); while(!(furi_hal_subghz_is_async_tx_complete())) { @@ -84,6 +87,9 @@ static void jukebox_send_signal(uint32_t frequency, string_t signal, string_t pr fflush(stdout); osDelay(333); } + notification_message(notification, &sequence_reset_vibro); + + furi_record_close("notification"); furi_hal_subghz_stop_async_tx(); furi_hal_subghz_sleep(); @@ -159,13 +165,15 @@ static void jukebox_render_callback(Canvas* canvas, void* ctx) { } canvas_draw_str(canvas, 10, 63, "[back] - skip, hold to exit"); - + jukebox_reset_state(state); release_mutex((ValueMutex*)ctx, state); } static void jukebox_input_callback(InputEvent* input_event, void* ctx) { - osMessageQueueId_t event_queue = ctx; - osMessageQueuePut(event_queue, input_event, 0, osWaitForever); + if (input_event->type == InputTypeRelease) { + osMessageQueueId_t event_queue = ctx; + osMessageQueuePut(event_queue, input_event, 0, osWaitForever); + } } int32_t jukebox_app(void* p) { @@ -218,7 +226,7 @@ int32_t jukebox_app(void* p) { ValueMutex state_mutex; if(!init_mutex(&state_mutex, &_state, sizeof(RemoteAppState))) { FURI_LOG_D(TAG, "cannot create mutex"); - return 0; + return(0); } ViewPort* view_port = view_port_alloc(); @@ -240,57 +248,22 @@ int32_t jukebox_app(void* p) { input_get_type_name(event.type)); if(event.key == InputKeyRight) { - if(event.type == InputTypePress) { state->press[0] = true; - } else if(event.type == InputTypeRelease) { - state->press[0] = false; - } else if(event.type == InputTypeShort) { - state->press[0] = false; - } } else if(event.key == InputKeyLeft) { - if(event.type == InputTypePress) { state->press[1] = true; - } else if(event.type == InputTypeRelease) { - state->press[1] = false; - } else if(event.type == InputTypeShort) { - state->press[1] = false; - } } else if(event.key == InputKeyUp) { - if(event.type == InputTypePress) { state->press[2] = true; - } else if(event.type == InputTypeRelease) { - state->press[2] = false; - } else if(event.type == InputTypeShort) { - state->press[2] = false; - } } else if(event.key == InputKeyDown) { - if(event.type == InputTypePress) { state->press[3] = true; - } else if(event.type == InputTypeRelease) { - state->press[3] = false; - } else if(event.type == InputTypeShort) { - state->press[3] = false; - } } else if(event.key == InputKeyOk) { - if(event.type == InputTypePress) { state->press[4] = true; - } else if(event.type == InputTypeRelease) { - state->press[4] = false; - } else if(event.type == InputTypeShort) { - state->press[4] = false; - } } else if(event.key == InputKeyBack) { - if(event.type == InputTypeLong) { release_mutex(&state_mutex, state); break; - } else if(event.type == InputTypeShort) { - jukebox_reset_state(state); - } } release_mutex(&state_mutex, state); view_port_update(view_port); } - // remove & free all stuff created by app gui_remove_view_port(gui, view_port); view_port_free(view_port); @@ -299,5 +272,5 @@ int32_t jukebox_app(void* p) { furi_record_close("gui"); - return 0; + return(0); } \ No newline at end of file diff --git a/applications/universal_rf/universal_rf.c b/applications/universal_rf/universal_rf.c index d37c38350..ca21e5162 100644 --- a/applications/universal_rf/universal_rf.c +++ b/applications/universal_rf/universal_rf.c @@ -7,6 +7,7 @@ #include #include #include +#include #define TAG "UniveralRFRemote" @@ -31,16 +32,15 @@ static char* subString(char* someString, int n) { char* new = malloc(sizeof(char) * n + 1); strncpy(new, someString, n); new[n] = '\0'; - return new; + return(new); } static char* file_stub(const char* file_name) { string_t filename; string_init(filename); - // string_init(file_name); path_extract_filename_no_ext(file_name, filename); - return subString((char*)string_get_cstr(filename), 8); + return(subString((char*)string_get_cstr(filename), 8)); } static void remote_send_signal(uint32_t frequency, string_t signal, string_t protocol) { @@ -58,7 +58,7 @@ static void remote_send_signal(uint32_t frequency, string_t signal, string_t pro } else { return; } - + NotificationApp* notification = furi_record_open("notification"); FlipperFormat* flipper_format = flipper_format_string_alloc(); Stream* stream = flipper_format_get_raw_stream(flipper_format); stream_clean(stream); @@ -77,6 +77,8 @@ static void remote_send_signal(uint32_t frequency, string_t signal, string_t pro TAG, "Transmitting at %lu, repeat %lu. Press CTRL+C to stop\r\n", frequency, repeat); furi_hal_power_suppress_charge_enter(); + notification_message(notification, &sequence_set_vibro_on); + furi_hal_subghz_start_async_tx(subghz_transmitter_yield, transmitter); while(!(furi_hal_subghz_is_async_tx_complete())) { @@ -84,6 +86,10 @@ static void remote_send_signal(uint32_t frequency, string_t signal, string_t pro fflush(stdout); osDelay(333); } + notification_message(notification, &sequence_reset_vibro); + + furi_record_close("notification"); + furi_hal_subghz_stop_async_tx(); furi_hal_subghz_sleep(); @@ -115,7 +121,6 @@ static void remote_render_callback(Canvas* canvas, void* ctx) { canvas_draw_str(canvas, 0, 36, strings[3]); canvas_draw_str(canvas, 85, 36, strings[4]); canvas_draw_str(canvas, 0, 48, strings[0]); - // canvas_draw_circle(canvas, 100, 26, 25); if(state->press[0]) { string_cat_printf(signal, "%s", string_get_cstr(right_file)); @@ -159,13 +164,15 @@ static void remote_render_callback(Canvas* canvas, void* ctx) { } canvas_draw_str(canvas, 10, 63, "[back] - skip, hold to exit"); - + remote_reset_state(state); release_mutex((ValueMutex*)ctx, state); } static void remote_input_callback(InputEvent* input_event, void* ctx) { - osMessageQueueId_t event_queue = ctx; - osMessageQueuePut(event_queue, input_event, 0, osWaitForever); + if (input_event->type == InputTypeRelease) { + osMessageQueueId_t event_queue = ctx; + osMessageQueuePut(event_queue, input_event, 0, osWaitForever); + } } int32_t universal_rf_remote_app(void* p) { @@ -218,7 +225,7 @@ int32_t universal_rf_remote_app(void* p) { ValueMutex state_mutex; if(!init_mutex(&state_mutex, &_state, sizeof(RemoteAppState))) { FURI_LOG_D(TAG, "cannot create mutex"); - return 0; + return(0); } ViewPort* view_port = view_port_alloc(); @@ -226,7 +233,6 @@ int32_t universal_rf_remote_app(void* p) { view_port_draw_callback_set(view_port, remote_render_callback, &state_mutex); view_port_input_callback_set(view_port, remote_input_callback, event_queue); - // Open GUI and register view_port Gui* gui = furi_record_open("gui"); gui_add_view_port(gui, view_port, GuiLayerFullscreen); @@ -240,58 +246,23 @@ int32_t universal_rf_remote_app(void* p) { input_get_type_name(event.type)); if(event.key == InputKeyRight) { - if(event.type == InputTypePress) { state->press[0] = true; - } else if(event.type == InputTypeRelease) { - state->press[0] = false; - } else if(event.type == InputTypeShort) { - state->press[0] = false; - } } else if(event.key == InputKeyLeft) { - if(event.type == InputTypePress) { state->press[1] = true; - } else if(event.type == InputTypeRelease) { - state->press[1] = false; - } else if(event.type == InputTypeShort) { - state->press[1] = false; - } } else if(event.key == InputKeyUp) { - if(event.type == InputTypePress) { state->press[2] = true; - } else if(event.type == InputTypeRelease) { - state->press[2] = false; - } else if(event.type == InputTypeShort) { - state->press[2] = false; - } } else if(event.key == InputKeyDown) { - if(event.type == InputTypePress) { state->press[3] = true; - } else if(event.type == InputTypeRelease) { - state->press[3] = false; - } else if(event.type == InputTypeShort) { - state->press[3] = false; - } } else if(event.key == InputKeyOk) { - if(event.type == InputTypePress) { state->press[4] = true; - } else if(event.type == InputTypeRelease) { - state->press[4] = false; - } else if(event.type == InputTypeShort) { - state->press[4] = false; - } } else if(event.key == InputKeyBack) { - if(event.type == InputTypeLong) { release_mutex(&state_mutex, state); break; - } else if(event.type == InputTypeShort) { - remote_reset_state(state); - } } release_mutex(&state_mutex, state); view_port_update(view_port); } - // remove & free all stuff created by app gui_remove_view_port(gui, view_port); view_port_free(view_port); osMessageQueueDelete(event_queue); @@ -299,5 +270,5 @@ int32_t universal_rf_remote_app(void* p) { furi_record_close("gui"); - return 0; -} + return(0); +} \ No newline at end of file diff --git a/assets/compiled/assets_icons.c b/assets/compiled/assets_icons.c index 9046dddd7..049f74adb 100644 --- a/assets/compiled/assets_icons.c +++ b/assets/compiled/assets_icons.c @@ -40,6 +40,9 @@ const uint8_t* const _I_125_10px[] = {_I_125_10px_0}; const uint8_t _I_Nfc_10px_0[] = {0x00,0x80,0x00,0x00,0x01,0x22,0x02,0x43,0x02,0x45,0x02,0x49,0x02,0x31,0x02,0x22,0x02,0x00,0x01,0x80,0x00,}; const uint8_t* const _I_Nfc_10px[] = {_I_Nfc_10px_0}; +const uint8_t _I_back_10px_0[] = {0x00,0x00,0x00,0x10,0x00,0x38,0x00,0x7C,0x00,0xFE,0x00,0x38,0x00,0x38,0x00,0xF8,0x01,0xF8,0x01,0x00,0x00,}; +const uint8_t* const _I_back_10px[] = {_I_back_10px_0}; + const uint8_t _I_badusb_10px_0[] = {0x01,0x00,0x11,0x00,0x00,0x0f,0xe2,0x01,0xfc,0x80,0xdd,0x20,0x32,0x48,0x08,0x14,0x40,0x23,0xa8,0x08,0xa0,}; const uint8_t* const _I_badusb_10px[] = {_I_badusb_10px_0}; @@ -55,6 +58,9 @@ const uint8_t* const _I_ibutt_10px[] = {_I_ibutt_10px_0}; const uint8_t _I_ir_10px_0[] = {0x00,0xFC,0x00,0x02,0x01,0x79,0x02,0x84,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x58,0x00,0x78,0x00,0xFF,0x03,}; const uint8_t* const _I_ir_10px[] = {_I_ir_10px_0}; +const uint8_t _I_loading_10px_0[] = {0x00,0xFE,0x00,0x82,0x00,0xBA,0x00,0x54,0x00,0x28,0x00,0x28,0x00,0x44,0x00,0x92,0x00,0xBA,0x00,0xFE,0x00,}; +const uint8_t* const _I_loading_10px[] = {_I_loading_10px_0}; + const uint8_t _I_sub1_10px_0[] = {0x01,0x00,0x12,0x00,0x81,0x40,0x69,0x30,0x2c,0x2c,0x0b,0x6a,0x01,0x28,0x0c,0x0a,0x65,0x01,0x98,0x40,0x00,0x26,}; const uint8_t* const _I_sub1_10px[] = {_I_sub1_10px_0}; @@ -316,6 +322,16 @@ const uint8_t _A_Bluetooth_14_4[] = {0x00,0x10,0x00,0x30,0x00,0x51,0x00,0x92,0x0 const uint8_t _A_Bluetooth_14_5[] = {0x00,0x10,0x00,0x30,0x00,0x51,0x00,0x92,0x00,0x94,0x04,0x58,0x08,0x30,0x09,0x30,0x09,0x58,0x08,0x94,0x04,0x92,0x00,0x51,0x00,0x30,0x00,0x10,0x00,}; const uint8_t* const _A_Bluetooth_14[] = {_A_Bluetooth_14_0,_A_Bluetooth_14_1,_A_Bluetooth_14_2,_A_Bluetooth_14_3,_A_Bluetooth_14_4,_A_Bluetooth_14_5}; +const uint8_t _A_Clock_14_0[] = {0x00,0xF0,0x01,0x0E,0x0E,0x47,0x1C,0x03,0x18,0x49,0x12,0x41,0x10,0x41,0x10,0x01,0x10,0x09,0x12,0x02,0x08,0x46,0x0C,0x0E,0x0E,0xF6,0x0D,0x02,0x08,}; +const uint8_t _A_Clock_14_1[] = {0x00,0xF0,0x01,0x0E,0x0E,0x47,0x1C,0x03,0x18,0x09,0x13,0x81,0x10,0x41,0x10,0x01,0x10,0x09,0x12,0x02,0x08,0x46,0x0C,0x0E,0x0E,0xF6,0x0D,0x02,0x08,}; +const uint8_t _A_Clock_14_2[] = {0x00,0xF0,0x01,0x0E,0x0E,0x47,0x1C,0x03,0x18,0x09,0x12,0x01,0x10,0xC1,0x11,0x01,0x10,0x09,0x12,0x02,0x08,0x46,0x0C,0x0E,0x0E,0xF6,0x0D,0x02,0x08,}; +const uint8_t _A_Clock_14_3[] = {0x00,0xF0,0x01,0x0E,0x0E,0x47,0x1C,0x03,0x18,0x09,0x12,0x01,0x10,0x41,0x10,0x81,0x10,0x09,0x13,0x02,0x08,0x46,0x0C,0x0E,0x0E,0xF6,0x0D,0x02,0x08,}; +const uint8_t _A_Clock_14_4[] = {0x00,0xF0,0x01,0x0E,0x0E,0x47,0x1C,0x03,0x18,0x09,0x12,0x01,0x10,0x41,0x10,0x41,0x10,0x49,0x12,0x02,0x08,0x46,0x0C,0x0E,0x0E,0xF6,0x0D,0x02,0x08,}; +const uint8_t _A_Clock_14_5[] = {0x00,0xF0,0x01,0x0E,0x0E,0x47,0x1C,0x03,0x18,0x09,0x12,0x01,0x10,0x41,0x10,0x21,0x10,0x19,0x12,0x02,0x08,0x46,0x0C,0x0E,0x0E,0xF6,0x0D,0x02,0x08,}; +const uint8_t _A_Clock_14_6[] = {0x00,0xF0,0x01,0x0E,0x0E,0x47,0x1C,0x03,0x18,0x09,0x12,0x01,0x10,0x71,0x10,0x01,0x10,0x09,0x12,0x02,0x08,0x46,0x0C,0x0E,0x0E,0xF6,0x0D,0x02,0x08,}; +const uint8_t _A_Clock_14_7[] = {0x00,0xF0,0x01,0x0E,0x0E,0x47,0x1C,0x03,0x18,0x19,0x12,0x21,0x10,0x41,0x10,0x01,0x10,0x09,0x12,0x02,0x08,0x46,0x0C,0x0E,0x0E,0xF6,0x0D,0x02,0x08,}; +const uint8_t* const _A_Clock_14[] = {_A_Clock_14_0,_A_Clock_14_1,_A_Clock_14_2,_A_Clock_14_3,_A_Clock_14_4,_A_Clock_14_5,_A_Clock_14_6,_A_Clock_14_7}; + const uint8_t _A_Debug_14_0[] = {0x00,0x20,0x01,0xC1,0x20,0x22,0x11,0x24,0x09,0xD9,0x26,0x16,0x1A,0xD8,0x06,0xD8,0x06,0xD6,0x1A,0x19,0x26,0xE4,0x09,0xC2,0x10,0x01,0x20,0x00,0x00,}; const uint8_t _A_Debug_14_1[] = {0x00,0x20,0x01,0xC0,0x00,0x22,0x11,0x25,0x29,0xD8,0x06,0x16,0x1A,0xD9,0x26,0xD8,0x06,0xD4,0x0A,0x12,0x12,0xEA,0x15,0xC5,0x28,0x02,0x10,0x02,0x10,}; const uint8_t _A_Debug_14_2[] = {0x00,0x20,0x01,0xC0,0x00,0x20,0x01,0x24,0x09,0xDA,0x16,0x11,0x22,0xDC,0x0E,0xDA,0x16,0xD9,0x26,0x14,0x0A,0xF2,0x13,0xD1,0x22,0x08,0x04,0x06,0x18,}; @@ -363,6 +379,13 @@ const uint8_t _A_Infrared_14_4[] = {0x01,0x00,0x0e,0x00,0x00,0x5f,0x82,0x02,0x05 const uint8_t _A_Infrared_14_5[] = {0x01,0x00,0x15,0x00,0x00,0x2f,0xc2,0x07,0x08,0x82,0x01,0x47,0xc1,0x01,0x05,0x98,0x14,0x41,0xa3,0xf8,0x83,0x80,0x47,0xff,0x3f,}; const uint8_t* const _A_Infrared_14[] = {_A_Infrared_14_0,_A_Infrared_14_1,_A_Infrared_14_2,_A_Infrared_14_3,_A_Infrared_14_4,_A_Infrared_14_5}; +const uint8_t _A_MusicPlayer_14_0[] = {0x01,0x00,0x17,0x00,0x00,0x1e,0x02,0x01,0xc0,0x80,0xf0,0x20,0x74,0x08,0x15,0x02,0x00,0x01,0x3b,0x84,0x02,0xf0,0x01,0x29,0x80,0x5c,0x80,}; +const uint8_t _A_MusicPlayer_14_1[] = {0x01,0x00,0x16,0x00,0x80,0x41,0x20,0x10,0xe8,0x04,0x7a,0x01,0x12,0x80,0x40,0x80,0x27,0x80,0x81,0xf0,0x00,0x25,0x80,0x80,0x86,0x94,}; +const uint8_t _A_MusicPlayer_14_2[] = {0x01,0x00,0x13,0x00,0x00,0x34,0x82,0x03,0x30,0x81,0xcc,0x20,0x51,0x08,0x00,0x05,0x63,0x90,0x08,0xf0,0x04,0xa1,0x80,}; +const uint8_t _A_MusicPlayer_14_3[] = {0x01,0x00,0x16,0x00,0x82,0x40,0x21,0xd0,0x08,0xf4,0x02,0x25,0x00,0x81,0x00,0x52,0x07,0x20,0x81,0xcc,0x00,0x23,0x01,0x90,0x06,0xd4,}; +const uint8_t _A_MusicPlayer_14_4[] = {0x01,0x00,0x15,0x00,0x00,0x2c,0x82,0x01,0x70,0x80,0x7c,0x20,0x19,0x08,0x04,0x40,0x02,0x91,0xc8,0x04,0x78,0x02,0x50,0xc8,0x00,}; +const uint8_t* const _A_MusicPlayer_14[] = {_A_MusicPlayer_14_0,_A_MusicPlayer_14_1,_A_MusicPlayer_14_2,_A_MusicPlayer_14_3,_A_MusicPlayer_14_4}; + const uint8_t _A_NFC_14_0[] = {0x00,0x00,0x08,0x00,0x10,0x00,0x12,0x00,0x22,0x42,0x24,0x87,0x24,0x8D,0x24,0x99,0x24,0xF1,0x24,0x62,0x24,0x00,0x22,0x00,0x12,0x00,0x10,0x00,0x08,}; const uint8_t _A_NFC_14_1[] = {0x01,0x00,0x1a,0x00,0x80,0x42,0x20,0x11,0x00,0x09,0x48,0x28,0x52,0x0c,0x3c,0x83,0x1b,0x20,0xcc,0xc8,0x3e,0x32,0x0b,0x14,0x80,0x1a,0x21,0x34,0x84,0x00,}; const uint8_t _A_NFC_14_2[] = {0x01,0x00,0x10,0x00,0x00,0x3d,0x0a,0x01,0x87,0x80,0x63,0x60,0x19,0x98,0x07,0xc6,0x01,0x62,0x09,0xc0,}; @@ -423,12 +446,54 @@ const uint8_t _A_Tamagotchi_14_4[] = {0x00,0xF0,0x03,0x08,0x06,0x04,0x0C,0x04,0x const uint8_t _A_Tamagotchi_14_5[] = {0x00,0xF0,0x03,0x08,0x06,0x04,0x0C,0x04,0x0C,0xF2,0x19,0x5A,0x1A,0xA9,0x32,0x49,0x33,0xF1,0x31,0x01,0x30,0x52,0x39,0x02,0x18,0x0C,0x0E,0xF0,0x07,}; const uint8_t* const _A_Tamagotchi_14[] = {_A_Tamagotchi_14_0,_A_Tamagotchi_14_1,_A_Tamagotchi_14_2,_A_Tamagotchi_14_3,_A_Tamagotchi_14_4,_A_Tamagotchi_14_5}; +const uint8_t _A_TouchTunes_14_0[] = {0x00,0x60,0x01,0xF8,0x07,0xF8,0x07,0x08,0x04,0x18,0x02,0x08,0x04,0x04,0x08,0x0C,0x0C,0x18,0x04,0x10,0x02,0x98,0x06,0xF8,0x07,0xF8,0x07,0x80,0x01,}; +const uint8_t _A_TouchTunes_14_1[] = {0x00,0x60,0x01,0xF8,0x07,0xF8,0x07,0x08,0x04,0x18,0x02,0x08,0x04,0x04,0x08,0x0C,0x0C,0x18,0x04,0x10,0x02,0x98,0x06,0xF8,0x07,0xF8,0x07,0x80,0x01,}; +const uint8_t _A_TouchTunes_14_2[] = {0x00,0x60,0x01,0xF8,0x07,0xF8,0x07,0x08,0x04,0x18,0x02,0x08,0x04,0x04,0x08,0x0C,0x0C,0x18,0x04,0x10,0x02,0x98,0x06,0xF8,0x07,0xF8,0x07,0x80,0x01,}; +const uint8_t _A_TouchTunes_14_3[] = {0x01,0x00,0x1a,0x00,0xd0,0x40,0x7f,0x10,0x70,0x08,0xc2,0x20,0x91,0x08,0x10,0x0c,0x60,0x90,0x88,0x64,0x32,0x30,0x09,0x2c,0xc4,0x18,0x26,0x78,0x08,0x08,}; +const uint8_t _A_TouchTunes_14_4[] = {0x00,0x40,0x01,0xF8,0x07,0xF8,0x07,0x18,0x04,0x10,0x06,0x08,0x04,0x0C,0x0C,0x08,0x04,0x18,0x02,0x10,0x06,0xD8,0x06,0xF8,0x07,0xF8,0x07,0x00,0x00,}; +const uint8_t _A_TouchTunes_14_5[] = {0x00,0x00,0x00,0xF0,0x07,0xF8,0x07,0x18,0x04,0x18,0x06,0x10,0x02,0x08,0x04,0x08,0x04,0x10,0x02,0x18,0x06,0xD8,0x06,0xF8,0x07,0xF0,0x03,0x00,0x00,}; +const uint8_t _A_TouchTunes_14_6[] = {0x00,0x00,0x01,0xF0,0x03,0xF8,0x07,0x18,0x04,0x10,0x02,0x18,0x04,0x0C,0x0C,0x08,0x04,0x18,0x02,0x10,0x02,0xF8,0x06,0xF8,0x07,0xF0,0x07,0x00,0x00,}; +const uint8_t _A_TouchTunes_14_7[] = {0x00,0x00,0x00,0xF8,0x07,0xF8,0x07,0x00,0x04,0x10,0x04,0x08,0x04,0x0C,0x0C,0x0C,0x0C,0x18,0x04,0x00,0x04,0xD8,0x06,0xF8,0x07,0xF0,0x03,0x00,0x00,}; +const uint8_t _A_TouchTunes_14_8[] = {0x00,0x00,0x00,0xF0,0x07,0xF8,0x07,0x18,0x04,0x08,0x04,0x0C,0x0C,0x0A,0x1C,0x0E,0x18,0x08,0x0C,0x48,0x00,0xD8,0x06,0xF8,0x07,0xF0,0x03,0x40,0x00,}; +const uint8_t _A_TouchTunes_14_9[] = {0x00,0x20,0x02,0xF8,0x07,0xF8,0x07,0x08,0x04,0x00,0x04,0x0C,0x0C,0x0B,0x34,0x0E,0x1C,0x0C,0x0C,0xE0,0x01,0xD8,0x06,0xF8,0x07,0xF8,0x07,0x00,0x00,}; +const uint8_t _A_TouchTunes_14_10[] = {0x00,0xA0,0x02,0xF8,0x07,0xF8,0x07,0x00,0x04,0x08,0x00,0x0C,0x0C,0x0B,0x34,0x0A,0x14,0x0C,0x0C,0xF0,0x01,0x98,0x05,0xF8,0x07,0xF8,0x07,0x40,0x00,}; +const uint8_t _A_TouchTunes_14_11[] = {0x00,0xA0,0x01,0xF8,0x07,0xF8,0x07,0x00,0x00,0x00,0x00,0x0E,0x0C,0x0A,0x34,0x0B,0x14,0x0C,0x0C,0xE0,0x01,0x98,0x05,0xF8,0x07,0xD8,0x07,0x60,0x00,}; +const uint8_t _A_TouchTunes_14_12[] = {0x00,0xA0,0x02,0xF8,0x07,0xF8,0x07,0x00,0x04,0x04,0x00,0x0C,0x0C,0x0A,0x14,0x0A,0x1C,0x0C,0x0C,0xE0,0x01,0x98,0x05,0xF8,0x07,0xD8,0x07,0x60,0x00,}; +const uint8_t _A_TouchTunes_14_13[] = {0x00,0x60,0x02,0xF8,0x07,0xF8,0x07,0x00,0x04,0x04,0x08,0x0C,0x0C,0x0A,0x34,0x0A,0x14,0x0C,0x0C,0xE8,0x05,0x98,0x05,0xF8,0x07,0xF8,0x07,0x80,0x00,}; +const uint8_t _A_TouchTunes_14_14[] = {0x00,0x60,0x02,0xF8,0x07,0xF8,0x07,0x00,0x04,0x04,0x08,0x0C,0x0C,0x0A,0x34,0x0A,0x14,0x0C,0x0C,0xE8,0x05,0x98,0x05,0xF8,0x07,0xF8,0x07,0x80,0x00,}; +const uint8_t _A_TouchTunes_14_15[] = {0x00,0x60,0x02,0xF8,0x07,0xF8,0x07,0x00,0x04,0x04,0x08,0x0C,0x0C,0x0A,0x34,0x0A,0x14,0x0C,0x0C,0xE8,0x05,0x98,0x05,0xF8,0x07,0xF8,0x07,0x80,0x00,}; +const uint8_t _A_TouchTunes_14_16[] = {0x00,0x60,0x02,0xF8,0x07,0xF8,0x07,0x00,0x04,0x04,0x08,0x0C,0x0C,0x0A,0x34,0x0A,0x14,0x0C,0x0C,0xE8,0x05,0x98,0x05,0xF8,0x07,0xF8,0x07,0x80,0x00,}; +const uint8_t _A_TouchTunes_14_17[] = {0x00,0xA0,0x01,0xF8,0x07,0xF8,0x07,0x00,0x00,0x04,0x08,0x0C,0x0C,0x0A,0x14,0x0A,0x1C,0x0C,0x0C,0xE0,0x01,0x98,0x06,0xF8,0x07,0xD8,0x07,0x60,0x00,}; +const uint8_t _A_TouchTunes_14_18[] = {0x00,0x20,0x02,0xF8,0x07,0xF8,0x07,0x08,0x04,0x08,0x04,0x0E,0x0C,0x0A,0x34,0x0B,0x14,0x0C,0x0C,0xE4,0x03,0xD8,0x06,0xF8,0x07,0xF8,0x07,0x00,0x00,}; +const uint8_t _A_TouchTunes_14_19[] = {0x00,0x00,0x00,0xF0,0x07,0xF8,0x07,0x18,0x04,0x04,0x08,0x0E,0x1C,0x0B,0x34,0x0A,0x14,0x0C,0x0C,0xE0,0x03,0xF8,0x06,0xF8,0x07,0xF0,0x07,0x00,0x00,}; +const uint8_t _A_TouchTunes_14_20[] = {0x00,0x00,0x01,0xF0,0x03,0xF8,0x07,0x18,0x04,0x04,0x00,0x0C,0x0C,0x0B,0x34,0x0B,0x34,0x0C,0x0C,0xE0,0x05,0xF8,0x06,0xF8,0x07,0xF0,0x03,0x00,0x00,}; +const uint8_t _A_TouchTunes_14_21[] = {0x00,0x00,0x00,0xF8,0x07,0xF8,0x07,0x10,0x00,0x08,0x04,0x0C,0x0C,0x0C,0x0C,0x0E,0x0C,0x08,0x04,0x88,0x04,0xF0,0x02,0xF8,0x07,0xF0,0x03,0x00,0x00,}; +const uint8_t _A_TouchTunes_14_22[] = {0x00,0x00,0x00,0xF8,0x07,0xF8,0x07,0x00,0x00,0x18,0x04,0x08,0x04,0x0C,0x0C,0x08,0x04,0x08,0x04,0x00,0x04,0xD8,0x02,0xF8,0x07,0xF0,0x03,0x00,0x00,}; +const uint8_t _A_TouchTunes_14_23[] = {0x01,0x00,0x14,0x00,0xa0,0x40,0x7f,0x10,0x70,0x08,0x81,0xc6,0x21,0x02,0x84,0x41,0x00,0x2a,0x09,0x1e,0xc4,0x18,0x26,0xa0,}; +const uint8_t _A_TouchTunes_14_24[] = {0x01,0x00,0x17,0x00,0x90,0x40,0xbf,0x10,0x70,0x08,0xc2,0x20,0x91,0x08,0x14,0x62,0x0c,0x05,0x10,0x3a,0xe6,0x20,0xa1,0x33,0xa0,0x40,0x00,}; +const uint8_t _A_TouchTunes_14_25[] = {0x01,0x00,0x1a,0x00,0xb0,0x40,0x7f,0x10,0x70,0x08,0xc2,0x20,0x91,0x88,0x34,0x62,0x04,0x05,0x10,0x39,0x40,0xa2,0x10,0x6c,0xc4,0x14,0x26,0x78,0x08,0x00,}; +const uint8_t _A_TouchTunes_14_26[] = {0x00,0x60,0x01,0xF8,0x07,0xF8,0x07,0x08,0x04,0x10,0x06,0x08,0x04,0x0C,0x0C,0x08,0x04,0x18,0x02,0x10,0x06,0x98,0x04,0xF8,0x07,0xF8,0x07,0x80,0x02,}; +const uint8_t _A_TouchTunes_14_27[] = {0x00,0x60,0x01,0xF8,0x07,0xF8,0x07,0x08,0x04,0x18,0x02,0x08,0x04,0x04,0x08,0x0C,0x0C,0x18,0x04,0x10,0x02,0x98,0x06,0xF8,0x07,0xF8,0x07,0x80,0x01,}; +const uint8_t _A_TouchTunes_14_28[] = {0x00,0x60,0x01,0xF8,0x07,0xF8,0x07,0x08,0x04,0x18,0x02,0x08,0x04,0x04,0x08,0x0C,0x0C,0x18,0x04,0x10,0x02,0x98,0x06,0xF8,0x07,0xF8,0x07,0x80,0x01,}; +const uint8_t _A_TouchTunes_14_29[] = {0x00,0x60,0x01,0xF8,0x07,0xF8,0x07,0x08,0x04,0x18,0x02,0x08,0x04,0x04,0x08,0x0C,0x0C,0x18,0x04,0x10,0x02,0x98,0x06,0xF8,0x07,0xF8,0x07,0x80,0x01,}; +const uint8_t* const _A_TouchTunes_14[] = {_A_TouchTunes_14_0,_A_TouchTunes_14_1,_A_TouchTunes_14_2,_A_TouchTunes_14_3,_A_TouchTunes_14_4,_A_TouchTunes_14_5,_A_TouchTunes_14_6,_A_TouchTunes_14_7,_A_TouchTunes_14_8,_A_TouchTunes_14_9,_A_TouchTunes_14_10,_A_TouchTunes_14_11,_A_TouchTunes_14_12,_A_TouchTunes_14_13,_A_TouchTunes_14_14,_A_TouchTunes_14_15,_A_TouchTunes_14_16,_A_TouchTunes_14_17,_A_TouchTunes_14_18,_A_TouchTunes_14_19,_A_TouchTunes_14_20,_A_TouchTunes_14_21,_A_TouchTunes_14_22,_A_TouchTunes_14_23,_A_TouchTunes_14_24,_A_TouchTunes_14_25,_A_TouchTunes_14_26,_A_TouchTunes_14_27,_A_TouchTunes_14_28,_A_TouchTunes_14_29}; + const uint8_t _A_U2F_14_0[] = {0x00,0x00,0x00,0x00,0x00,0xE0,0x01,0x10,0x02,0x08,0x04,0xFE,0x1F,0x01,0x20,0xD5,0x2D,0x55,0x25,0x15,0x2D,0x95,0x24,0xDD,0x25,0x01,0x20,0xFE,0x1F,}; const uint8_t _A_U2F_14_1[] = {0x00,0x00,0x00,0xE0,0x01,0x10,0x02,0x08,0x04,0x08,0x04,0xFE,0x1F,0x01,0x20,0xD5,0x2D,0x55,0x25,0x15,0x2D,0x95,0x24,0xDD,0x25,0x01,0x20,0xFE,0x1F,}; const uint8_t _A_U2F_14_2[] = {0x00,0xE0,0x01,0x10,0x02,0x08,0x04,0x08,0x04,0x08,0x00,0xFE,0x1F,0x01,0x20,0xD5,0x2D,0x55,0x25,0x15,0x2D,0x95,0x24,0xDD,0x25,0x01,0x20,0xFE,0x1F,}; const uint8_t _A_U2F_14_3[] = {0x00,0x00,0x00,0xE0,0x01,0x10,0x02,0x08,0x04,0x08,0x04,0xFE,0x1F,0x01,0x20,0xD5,0x2D,0x55,0x25,0x15,0x2D,0x95,0x24,0xDD,0x25,0x01,0x20,0xFE,0x1F,}; const uint8_t* const _A_U2F_14[] = {_A_U2F_14_0,_A_U2F_14_1,_A_U2F_14_2,_A_U2F_14_3}; +const uint8_t _A_UniversalRemote_14_0[] = {0x01,0x00,0x17,0x00,0xe0,0x40,0x24,0x10,0x1f,0x04,0x04,0x0e,0x20,0x31,0x8b,0x46,0xa2,0xb2,0xa0,0x08,0x81,0x44,0x1a,0xa1,0x51,0x0c,0x88,}; +const uint8_t _A_UniversalRemote_14_1[] = {0x01,0x00,0x17,0x00,0xe0,0x40,0x3c,0x10,0x10,0x08,0x81,0xc4,0x06,0x31,0x68,0xd4,0x56,0x54,0x01,0x10,0x28,0x83,0x56,0x41,0x01,0x0c,0x88,}; +const uint8_t _A_UniversalRemote_14_2[] = {0x01,0x00,0x17,0x00,0xe0,0x40,0x24,0x10,0x1f,0x04,0x04,0x0e,0x20,0x31,0x8b,0x46,0xa2,0xb3,0xa0,0x08,0x81,0x44,0x1a,0xa1,0x51,0x0c,0x88,}; +const uint8_t _A_UniversalRemote_14_3[] = {0x01,0x00,0x17,0x00,0xe0,0x40,0x24,0x10,0x1f,0x04,0x04,0x0e,0x20,0x31,0x8b,0x46,0xa2,0xb2,0xa0,0x08,0x81,0x44,0x1a,0xa0,0x11,0x0c,0x88,}; +const uint8_t _A_UniversalRemote_14_4[] = {0x01,0x00,0x17,0x00,0xe0,0x40,0x24,0x10,0x1f,0x04,0x04,0x0e,0x20,0x31,0x8b,0x46,0xa2,0xf2,0xa0,0x08,0x81,0x44,0x1a,0xa1,0x51,0x0c,0x88,}; +const uint8_t _A_UniversalRemote_14_5[] = {0x01,0x00,0x17,0x00,0xe0,0x40,0x3c,0x10,0x10,0x08,0x81,0xc4,0x06,0x31,0x68,0xd4,0x56,0x54,0x01,0x10,0x28,0x83,0x56,0x41,0x01,0x0c,0x88,}; +const uint8_t _A_UniversalRemote_14_6[] = {0x01,0x00,0x17,0x00,0xe0,0x40,0x24,0x10,0x1f,0x04,0x04,0x0e,0x20,0x31,0xfb,0x46,0xfe,0xb2,0xb0,0x08,0x81,0x44,0x1a,0xa1,0x51,0x0c,0x88,}; +const uint8_t* const _A_UniversalRemote_14[] = {_A_UniversalRemote_14_0,_A_UniversalRemote_14_1,_A_UniversalRemote_14_2,_A_UniversalRemote_14_3,_A_UniversalRemote_14_4,_A_UniversalRemote_14_5,_A_UniversalRemote_14_6}; + + const uint8_t _A_iButton_14_0[] = {0x00,0x00,0x1C,0x00,0x3E,0x00,0x35,0x80,0x3A,0x78,0x15,0x84,0x0A,0x32,0x05,0x49,0x02,0x85,0x02,0x85,0x02,0x49,0x02,0x32,0x01,0x84,0x00,0x78,0x00,}; const uint8_t _A_iButton_14_1[] = {0x00,0x00,0x00,0x00,0x38,0x00,0x26,0x80,0x21,0xE0,0x10,0x38,0x0D,0x6C,0x03,0x56,0x01,0x2B,0x01,0x97,0x00,0x4D,0x00,0x21,0x00,0x1E,0x00,0x00,0x00,}; const uint8_t _A_iButton_14_2[] = {0x01,0x00,0x1a,0x00,0x00,0x24,0xc2,0x01,0x2c,0x80,0x48,0xfb,0x11,0x89,0x64,0x1b,0x2d,0x01,0xa5,0xc0,0x24,0xb0,0x08,0x94,0x02,0x13,0x00,0x83,0x85,0x88,}; @@ -648,11 +713,13 @@ const Icon A_Levelup1_128x64 = {.width=128,.height=64,.frame_count=11,.frame_rat const Icon A_Levelup2_128x64 = {.width=128,.height=64,.frame_count=11,.frame_rate=2,.frames=_A_Levelup2_128x64}; const Icon I_125_10px = {.width=10,.height=10,.frame_count=1,.frame_rate=0,.frames=_I_125_10px}; const Icon I_Nfc_10px = {.width=10,.height=10,.frame_count=1,.frame_rate=0,.frames=_I_Nfc_10px}; +const Icon I_back_10px = {.width=10,.height=10,.frame_count=1,.frame_rate=0,.frames=_I_back_10px}; const Icon I_badusb_10px = {.width=10,.height=10,.frame_count=1,.frame_rate=0,.frames=_I_badusb_10px}; const Icon I_ble_10px = {.width=10,.height=10,.frame_count=1,.frame_rate=0,.frames=_I_ble_10px}; const Icon I_dir_10px = {.width=10,.height=10,.frame_count=1,.frame_rate=0,.frames=_I_dir_10px}; const Icon I_ibutt_10px = {.width=10,.height=10,.frame_count=1,.frame_rate=0,.frames=_I_ibutt_10px}; const Icon I_ir_10px = {.width=10,.height=10,.frame_count=1,.frame_rate=0,.frames=_I_ir_10px}; +const Icon I_loading_10px = {.width=10,.height=10,.frame_count=1,.frame_rate=0,.frames=_I_loading_10px}; const Icon I_sub1_10px = {.width=10,.height=10,.frame_count=1,.frame_rate=0,.frames=_I_sub1_10px}; const Icon I_u2f_10px = {.width=10,.height=10,.frame_count=1,.frame_rate=0,.frames=_I_u2f_10px}; const Icon I_unknown_10px = {.width=10,.height=10,.frame_count=1,.frame_rate=0,.frames=_I_unknown_10px}; @@ -732,11 +799,13 @@ const Icon I_KeySave_24x11 = {.width=24,.height=11,.frame_count=1,.frame_rate=0, const Icon A_125khz_14 = {.width=14,.height=14,.frame_count=4,.frame_rate=3,.frames=_A_125khz_14}; const Icon A_BadUsb_14 = {.width=14,.height=14,.frame_count=11,.frame_rate=3,.frames=_A_BadUsb_14}; const Icon A_Bluetooth_14 = {.width=14,.height=14,.frame_count=6,.frame_rate=3,.frames=_A_Bluetooth_14}; +const Icon A_Clock_14 = {.width=14,.height=14,.frame_count=8,.frame_rate=3,.frames=_A_Clock_14}; const Icon A_Debug_14 = {.width=14,.height=14,.frame_count=4,.frame_rate=3,.frames=_A_Debug_14}; const Icon A_FileManager_14 = {.width=14,.height=14,.frame_count=10,.frame_rate=3,.frames=_A_FileManager_14}; const Icon A_GPIO_14 = {.width=14,.height=14,.frame_count=8,.frame_rate=3,.frames=_A_GPIO_14}; const Icon A_Games_14 = {.width=14,.height=14,.frame_count=9,.frame_rate=3,.frames=_A_Games_14}; const Icon A_Infrared_14 = {.width=14,.height=14,.frame_count=6,.frame_rate=3,.frames=_A_Infrared_14}; +const Icon A_MusicPlayer_14 = {.width=14,.height=14,.frame_count=5,.frame_rate=3,.frames=_A_MusicPlayer_14}; const Icon A_NFC_14 = {.width=14,.height=14,.frame_count=4,.frame_rate=3,.frames=_A_NFC_14}; const Icon A_Passport_14 = {.width=14,.height=14,.frame_count=10,.frame_rate=3,.frames=_A_Passport_14}; const Icon A_Plugins_14 = {.width=14,.height=14,.frame_count=9,.frame_rate=3,.frames=_A_Plugins_14}; @@ -744,7 +813,9 @@ const Icon A_Power_14 = {.width=14,.height=14,.frame_count=1,.frame_rate=3,.fram const Icon A_Settings_14 = {.width=14,.height=14,.frame_count=10,.frame_rate=3,.frames=_A_Settings_14}; const Icon A_Sub1ghz_14 = {.width=14,.height=14,.frame_count=6,.frame_rate=3,.frames=_A_Sub1ghz_14}; const Icon A_Tamagotchi_14 = {.width=14,.height=14,.frame_count=6,.frame_rate=3,.frames=_A_Tamagotchi_14}; +const Icon A_TouchTunes_14 = {.width=14,.height=14,.frame_count=30,.frame_rate=3,.frames=_A_TouchTunes_14}; const Icon A_U2F_14 = {.width=14,.height=14,.frame_count=4,.frame_rate=3,.frames=_A_U2F_14}; +const Icon A_UniversalRemote_14 = {.width=14,.height=14,.frame_count=7,.frame_rate=3,.frames=_A_UniversalRemote_14}; const Icon A_iButton_14 = {.width=14,.height=14,.frame_count=7,.frame_rate=3,.frames=_A_iButton_14}; const Icon I_Detailed_chip_17x13 = {.width=17,.height=13,.frame_count=1,.frame_rate=0,.frames=_I_Detailed_chip_17x13}; const Icon I_Medium_chip_22x21 = {.width=22,.height=21,.frame_count=1,.frame_rate=0,.frames=_I_Medium_chip_22x21}; diff --git a/assets/compiled/assets_icons.h b/assets/compiled/assets_icons.h index 64cd3d36d..1c08545bf 100644 --- a/assets/compiled/assets_icons.h +++ b/assets/compiled/assets_icons.h @@ -7,11 +7,13 @@ extern const Icon A_Levelup1_128x64; extern const Icon A_Levelup2_128x64; extern const Icon I_125_10px; extern const Icon I_Nfc_10px; +extern const Icon I_back_10px; extern const Icon I_badusb_10px; extern const Icon I_ble_10px; extern const Icon I_dir_10px; extern const Icon I_ibutt_10px; extern const Icon I_ir_10px; +extern const Icon I_loading_10px; extern const Icon I_sub1_10px; extern const Icon I_u2f_10px; extern const Icon I_unknown_10px; @@ -91,11 +93,13 @@ extern const Icon I_KeySave_24x11; extern const Icon A_125khz_14; extern const Icon A_BadUsb_14; extern const Icon A_Bluetooth_14; +extern const Icon A_Clock_14; extern const Icon A_Debug_14; extern const Icon A_FileManager_14; extern const Icon A_GPIO_14; extern const Icon A_Games_14; extern const Icon A_Infrared_14; +extern const Icon A_MusicPlayer_14; extern const Icon A_NFC_14; extern const Icon A_Passport_14; extern const Icon A_Plugins_14; @@ -103,7 +107,9 @@ extern const Icon A_Power_14; extern const Icon A_Settings_14; extern const Icon A_Sub1ghz_14; extern const Icon A_Tamagotchi_14; +extern const Icon A_TouchTunes_14; extern const Icon A_U2F_14; +extern const Icon A_UniversalRemote_14; extern const Icon A_iButton_14; extern const Icon I_Detailed_chip_17x13; extern const Icon I_Medium_chip_22x21; diff --git a/assets/icons/Archive/back_10px.png b/assets/icons/Archive/back_10px.png new file mode 100644 index 000000000..008efe82f Binary files /dev/null and b/assets/icons/Archive/back_10px.png differ diff --git a/assets/icons/Archive/loading_10px.png b/assets/icons/Archive/loading_10px.png new file mode 100644 index 000000000..9cc33b7fd Binary files /dev/null and b/assets/icons/Archive/loading_10px.png differ diff --git a/assets/icons/MainMenu/TouchTunes_14/frame_00_delay-0.04s.png b/assets/icons/MainMenu/TouchTunes_14/frame_00_delay-0.04s.png new file mode 100644 index 000000000..d6c97146d Binary files /dev/null and b/assets/icons/MainMenu/TouchTunes_14/frame_00_delay-0.04s.png differ diff --git a/assets/icons/MainMenu/TouchTunes_14/frame_01_delay-0.04s.png b/assets/icons/MainMenu/TouchTunes_14/frame_01_delay-0.04s.png new file mode 100644 index 000000000..479fbd58b Binary files /dev/null and b/assets/icons/MainMenu/TouchTunes_14/frame_01_delay-0.04s.png differ diff --git a/assets/icons/MainMenu/TouchTunes_14/frame_02_delay-0.04s.png b/assets/icons/MainMenu/TouchTunes_14/frame_02_delay-0.04s.png new file mode 100644 index 000000000..479fbd58b Binary files /dev/null and b/assets/icons/MainMenu/TouchTunes_14/frame_02_delay-0.04s.png differ diff --git a/assets/icons/MainMenu/TouchTunes_14/frame_03_delay-0.04s.png b/assets/icons/MainMenu/TouchTunes_14/frame_03_delay-0.04s.png new file mode 100644 index 000000000..1ece97a6f Binary files /dev/null and b/assets/icons/MainMenu/TouchTunes_14/frame_03_delay-0.04s.png differ diff --git a/assets/icons/MainMenu/TouchTunes_14/frame_04_delay-0.04s.png b/assets/icons/MainMenu/TouchTunes_14/frame_04_delay-0.04s.png new file mode 100644 index 000000000..d85fa6ebc Binary files /dev/null and b/assets/icons/MainMenu/TouchTunes_14/frame_04_delay-0.04s.png differ diff --git a/assets/icons/MainMenu/TouchTunes_14/frame_05_delay-0.04s.png b/assets/icons/MainMenu/TouchTunes_14/frame_05_delay-0.04s.png new file mode 100644 index 000000000..1f5ff11cb Binary files /dev/null and b/assets/icons/MainMenu/TouchTunes_14/frame_05_delay-0.04s.png differ diff --git a/assets/icons/MainMenu/TouchTunes_14/frame_06_delay-0.04s.png b/assets/icons/MainMenu/TouchTunes_14/frame_06_delay-0.04s.png new file mode 100644 index 000000000..8fbf8625c Binary files /dev/null and b/assets/icons/MainMenu/TouchTunes_14/frame_06_delay-0.04s.png differ diff --git a/assets/icons/MainMenu/TouchTunes_14/frame_07_delay-0.04s.png b/assets/icons/MainMenu/TouchTunes_14/frame_07_delay-0.04s.png new file mode 100644 index 000000000..0472780e4 Binary files /dev/null and b/assets/icons/MainMenu/TouchTunes_14/frame_07_delay-0.04s.png differ diff --git a/assets/icons/MainMenu/TouchTunes_14/frame_08_delay-0.04s.png b/assets/icons/MainMenu/TouchTunes_14/frame_08_delay-0.04s.png new file mode 100644 index 000000000..f3724bbde Binary files /dev/null and b/assets/icons/MainMenu/TouchTunes_14/frame_08_delay-0.04s.png differ diff --git a/assets/icons/MainMenu/TouchTunes_14/frame_09_delay-0.04s.png b/assets/icons/MainMenu/TouchTunes_14/frame_09_delay-0.04s.png new file mode 100644 index 000000000..ff34450d9 Binary files /dev/null and b/assets/icons/MainMenu/TouchTunes_14/frame_09_delay-0.04s.png differ diff --git a/assets/icons/MainMenu/TouchTunes_14/frame_10_delay-0.04s.png b/assets/icons/MainMenu/TouchTunes_14/frame_10_delay-0.04s.png new file mode 100644 index 000000000..2b61340cb Binary files /dev/null and b/assets/icons/MainMenu/TouchTunes_14/frame_10_delay-0.04s.png differ diff --git a/assets/icons/MainMenu/TouchTunes_14/frame_11_delay-0.04s.png b/assets/icons/MainMenu/TouchTunes_14/frame_11_delay-0.04s.png new file mode 100644 index 000000000..9420c763f Binary files /dev/null and b/assets/icons/MainMenu/TouchTunes_14/frame_11_delay-0.04s.png differ diff --git a/assets/icons/MainMenu/TouchTunes_14/frame_12_delay-0.04s.png b/assets/icons/MainMenu/TouchTunes_14/frame_12_delay-0.04s.png new file mode 100644 index 000000000..66a096a64 Binary files /dev/null and b/assets/icons/MainMenu/TouchTunes_14/frame_12_delay-0.04s.png differ diff --git a/assets/icons/MainMenu/TouchTunes_14/frame_13_delay-0.04s.png b/assets/icons/MainMenu/TouchTunes_14/frame_13_delay-0.04s.png new file mode 100644 index 000000000..e9527fa83 Binary files /dev/null and b/assets/icons/MainMenu/TouchTunes_14/frame_13_delay-0.04s.png differ diff --git a/assets/icons/MainMenu/TouchTunes_14/frame_14_delay-0.04s.png b/assets/icons/MainMenu/TouchTunes_14/frame_14_delay-0.04s.png new file mode 100644 index 000000000..e9527fa83 Binary files /dev/null and b/assets/icons/MainMenu/TouchTunes_14/frame_14_delay-0.04s.png differ diff --git a/assets/icons/MainMenu/TouchTunes_14/frame_15_delay-0.04s.png b/assets/icons/MainMenu/TouchTunes_14/frame_15_delay-0.04s.png new file mode 100644 index 000000000..e9527fa83 Binary files /dev/null and b/assets/icons/MainMenu/TouchTunes_14/frame_15_delay-0.04s.png differ diff --git a/assets/icons/MainMenu/TouchTunes_14/frame_16_delay-0.04s.png b/assets/icons/MainMenu/TouchTunes_14/frame_16_delay-0.04s.png new file mode 100644 index 000000000..e9527fa83 Binary files /dev/null and b/assets/icons/MainMenu/TouchTunes_14/frame_16_delay-0.04s.png differ diff --git a/assets/icons/MainMenu/TouchTunes_14/frame_17_delay-0.04s.png b/assets/icons/MainMenu/TouchTunes_14/frame_17_delay-0.04s.png new file mode 100644 index 000000000..ff4374596 Binary files /dev/null and b/assets/icons/MainMenu/TouchTunes_14/frame_17_delay-0.04s.png differ diff --git a/assets/icons/MainMenu/TouchTunes_14/frame_18_delay-0.04s.png b/assets/icons/MainMenu/TouchTunes_14/frame_18_delay-0.04s.png new file mode 100644 index 000000000..2109273f0 Binary files /dev/null and b/assets/icons/MainMenu/TouchTunes_14/frame_18_delay-0.04s.png differ diff --git a/assets/icons/MainMenu/TouchTunes_14/frame_19_delay-0.04s.png b/assets/icons/MainMenu/TouchTunes_14/frame_19_delay-0.04s.png new file mode 100644 index 000000000..0c7e9eaa9 Binary files /dev/null and b/assets/icons/MainMenu/TouchTunes_14/frame_19_delay-0.04s.png differ diff --git a/assets/icons/MainMenu/TouchTunes_14/frame_20_delay-0.04s.png b/assets/icons/MainMenu/TouchTunes_14/frame_20_delay-0.04s.png new file mode 100644 index 000000000..4c283af6c Binary files /dev/null and b/assets/icons/MainMenu/TouchTunes_14/frame_20_delay-0.04s.png differ diff --git a/assets/icons/MainMenu/TouchTunes_14/frame_21_delay-0.04s.png b/assets/icons/MainMenu/TouchTunes_14/frame_21_delay-0.04s.png new file mode 100644 index 000000000..750aec162 Binary files /dev/null and b/assets/icons/MainMenu/TouchTunes_14/frame_21_delay-0.04s.png differ diff --git a/assets/icons/MainMenu/TouchTunes_14/frame_22_delay-0.04s.png b/assets/icons/MainMenu/TouchTunes_14/frame_22_delay-0.04s.png new file mode 100644 index 000000000..42fda5096 Binary files /dev/null and b/assets/icons/MainMenu/TouchTunes_14/frame_22_delay-0.04s.png differ diff --git a/assets/icons/MainMenu/TouchTunes_14/frame_23_delay-0.04s.png b/assets/icons/MainMenu/TouchTunes_14/frame_23_delay-0.04s.png new file mode 100644 index 000000000..17007aad5 Binary files /dev/null and b/assets/icons/MainMenu/TouchTunes_14/frame_23_delay-0.04s.png differ diff --git a/assets/icons/MainMenu/TouchTunes_14/frame_24_delay-0.04s.png b/assets/icons/MainMenu/TouchTunes_14/frame_24_delay-0.04s.png new file mode 100644 index 000000000..4c03a8101 Binary files /dev/null and b/assets/icons/MainMenu/TouchTunes_14/frame_24_delay-0.04s.png differ diff --git a/assets/icons/MainMenu/TouchTunes_14/frame_25_delay-0.04s.png b/assets/icons/MainMenu/TouchTunes_14/frame_25_delay-0.04s.png new file mode 100644 index 000000000..334498adc Binary files /dev/null and b/assets/icons/MainMenu/TouchTunes_14/frame_25_delay-0.04s.png differ diff --git a/assets/icons/MainMenu/TouchTunes_14/frame_26_delay-0.04s.png b/assets/icons/MainMenu/TouchTunes_14/frame_26_delay-0.04s.png new file mode 100644 index 000000000..b203dc302 Binary files /dev/null and b/assets/icons/MainMenu/TouchTunes_14/frame_26_delay-0.04s.png differ diff --git a/assets/icons/MainMenu/TouchTunes_14/frame_27_delay-0.04s.png b/assets/icons/MainMenu/TouchTunes_14/frame_27_delay-0.04s.png new file mode 100644 index 000000000..3e17c6c76 Binary files /dev/null and b/assets/icons/MainMenu/TouchTunes_14/frame_27_delay-0.04s.png differ diff --git a/assets/icons/MainMenu/TouchTunes_14/frame_28_delay-0.04s.png b/assets/icons/MainMenu/TouchTunes_14/frame_28_delay-0.04s.png new file mode 100644 index 000000000..479fbd58b Binary files /dev/null and b/assets/icons/MainMenu/TouchTunes_14/frame_28_delay-0.04s.png differ diff --git a/assets/icons/MainMenu/TouchTunes_14/frame_29_delay-0.04s.png b/assets/icons/MainMenu/TouchTunes_14/frame_29_delay-0.04s.png new file mode 100644 index 000000000..479fbd58b Binary files /dev/null and b/assets/icons/MainMenu/TouchTunes_14/frame_29_delay-0.04s.png differ diff --git a/assets/icons/MainMenu/TouchTunes_14/frame_rate b/assets/icons/MainMenu/TouchTunes_14/frame_rate new file mode 100644 index 000000000..e440e5c84 --- /dev/null +++ b/assets/icons/MainMenu/TouchTunes_14/frame_rate @@ -0,0 +1 @@ +3 \ No newline at end of file diff --git a/assets/icons/MainMenu/UniversalRemote_14/frame_1.png b/assets/icons/MainMenu/UniversalRemote_14/frame_1.png new file mode 100644 index 000000000..8d135853f Binary files /dev/null and b/assets/icons/MainMenu/UniversalRemote_14/frame_1.png differ diff --git a/assets/icons/MainMenu/UniversalRemote_14/frame_2.png b/assets/icons/MainMenu/UniversalRemote_14/frame_2.png new file mode 100644 index 000000000..24c349ead Binary files /dev/null and b/assets/icons/MainMenu/UniversalRemote_14/frame_2.png differ diff --git a/assets/icons/MainMenu/UniversalRemote_14/frame_3.png b/assets/icons/MainMenu/UniversalRemote_14/frame_3.png new file mode 100644 index 000000000..c64bad5f2 Binary files /dev/null and b/assets/icons/MainMenu/UniversalRemote_14/frame_3.png differ diff --git a/assets/icons/MainMenu/UniversalRemote_14/frame_4.png b/assets/icons/MainMenu/UniversalRemote_14/frame_4.png new file mode 100644 index 000000000..efd7181f0 Binary files /dev/null and b/assets/icons/MainMenu/UniversalRemote_14/frame_4.png differ diff --git a/assets/icons/MainMenu/UniversalRemote_14/frame_5.png b/assets/icons/MainMenu/UniversalRemote_14/frame_5.png new file mode 100644 index 000000000..300f00128 Binary files /dev/null and b/assets/icons/MainMenu/UniversalRemote_14/frame_5.png differ diff --git a/assets/icons/MainMenu/UniversalRemote_14/frame_6.png b/assets/icons/MainMenu/UniversalRemote_14/frame_6.png new file mode 100644 index 000000000..24c349ead Binary files /dev/null and b/assets/icons/MainMenu/UniversalRemote_14/frame_6.png differ diff --git a/assets/icons/MainMenu/UniversalRemote_14/frame_7.png b/assets/icons/MainMenu/UniversalRemote_14/frame_7.png new file mode 100644 index 000000000..d41a66495 Binary files /dev/null and b/assets/icons/MainMenu/UniversalRemote_14/frame_7.png differ diff --git a/lib/digital_signal/digital_signal.c b/lib/digital_signal/digital_signal.c new file mode 100644 index 000000000..b9be3b602 --- /dev/null +++ b/lib/digital_signal/digital_signal.c @@ -0,0 +1,173 @@ +#include "digital_signal.h" + +#include +#include +#include +#include + +#define F_TIM (64000000.0) +#define T_TIM (1.0 / F_TIM) + +#define MAX_ARR_BUFF_SIZE (10000) + +// TODO rework with dynamic allocation +static uint32_t digital_signal_arr_buff[MAX_ARR_BUFF_SIZE]; + +DigitalSignal* digital_signal_alloc(uint32_t max_edges_cnt) { + DigitalSignal* signal = malloc(sizeof(DigitalSignal)); + signal->start_level = true; + signal->edges_max_cnt = max_edges_cnt; + signal->edge_timings = malloc(max_edges_cnt * sizeof(float)); + signal->edge_cnt = 0; + + return signal; +} + +void digital_signal_free(DigitalSignal* signal) { + furi_assert(signal); + + free(signal->edge_timings); + free(signal); +} + +bool digital_signal_append(DigitalSignal* signal_a, DigitalSignal* signal_b) { + furi_assert(signal_a); + furi_assert(signal_b); + + if(signal_a->edges_max_cnt < signal_a->edge_cnt + signal_b->edge_cnt) { + return false; + } + + bool end_level = signal_a->start_level ^ !(signal_a->edge_cnt % 2); + uint8_t start_copy = 0; + if(end_level == signal_b->start_level) { + if(signal_a->edge_cnt) { + signal_a->edge_timings[signal_a->edge_cnt - 1] += signal_b->edge_timings[0]; + } else { + signal_a->edge_timings[signal_a->edge_cnt] += signal_b->edge_timings[0]; + } + start_copy += 1; + } + memcpy( + &signal_a->edge_timings[signal_a->edge_cnt], + &signal_b->edge_timings[start_copy], + (signal_b->edge_cnt - start_copy) * sizeof(float)); + signal_a->edge_cnt += signal_b->edge_cnt - start_copy; + + return true; +} + +bool digital_signal_get_start_level(DigitalSignal* signal) { + furi_assert(signal); + + return signal->start_level; +} + +uint32_t digital_signal_get_edges_cnt(DigitalSignal* signal) { + furi_assert(signal); + + return signal->edge_cnt; +} + +float digital_signal_get_edge(DigitalSignal* signal, uint32_t edge_num) { + furi_assert(signal); + furi_assert(edge_num < signal->edge_cnt); + + return signal->edge_timings[edge_num]; +} + +static void digital_signal_prepare_arr(DigitalSignal* signal, uint32_t* arr) { + float t_signal = 0; + float t_current = 0; + float r = 0; + float r_int = 0; + float r_dec = 0; + + for(size_t i = 0; i < signal->edge_cnt - 1; i++) { + t_signal += signal->edge_timings[i]; + r = (t_signal - t_current) / T_TIM; + r_dec = modff(r, &r_int); + if(r_dec < 0.5f) { + arr[i] = (uint32_t)r_int - 1; + } else { + arr[i] = (uint32_t)r_int; + } + t_current += (arr[i] + 1) * T_TIM; + } +} + +bool digital_signal_send(DigitalSignal* signal, const GpioPin* gpio) { + furi_assert(signal); + furi_assert(gpio); + + // Configure gpio as output + furi_hal_gpio_init(gpio, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); + + // Init gpio buffer and DMA channel + uint16_t gpio_reg = gpio->port->ODR; + uint16_t gpio_buff[2]; + if(signal->start_level) { + gpio_buff[0] = gpio_reg | gpio->pin; + gpio_buff[1] = gpio_reg & ~(gpio->pin); + } else { + gpio_buff[0] = gpio_reg & ~(gpio->pin); + gpio_buff[1] = gpio_reg | gpio->pin; + } + LL_DMA_InitTypeDef dma_config = {}; + dma_config.MemoryOrM2MDstAddress = (uint32_t)gpio_buff; + dma_config.PeriphOrM2MSrcAddress = (uint32_t) & (gpio->port->ODR); + dma_config.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH; + dma_config.Mode = LL_DMA_MODE_CIRCULAR; + dma_config.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT; + dma_config.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT; + dma_config.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_HALFWORD; + dma_config.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_HALFWORD; + dma_config.NbData = 2; + dma_config.PeriphRequest = LL_DMAMUX_REQ_TIM2_UP; + dma_config.Priority = LL_DMA_PRIORITY_VERYHIGH; + LL_DMA_Init(DMA1, LL_DMA_CHANNEL_1, &dma_config); + LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_1, 2); + LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_1); + + // Init timer arr register buffer and DMA channel + digital_signal_prepare_arr(signal, digital_signal_arr_buff); + dma_config.MemoryOrM2MDstAddress = (uint32_t)digital_signal_arr_buff; + dma_config.PeriphOrM2MSrcAddress = (uint32_t) & (TIM2->ARR); + dma_config.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH; + dma_config.Mode = LL_DMA_MODE_NORMAL; + dma_config.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT; + dma_config.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT; + dma_config.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_WORD; + dma_config.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_WORD; + dma_config.NbData = signal->edge_cnt - 2; + dma_config.PeriphRequest = LL_DMAMUX_REQ_TIM2_UP; + dma_config.Priority = LL_DMA_PRIORITY_HIGH; + LL_DMA_Init(DMA1, LL_DMA_CHANNEL_2, &dma_config); + LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_2, signal->edge_cnt - 2); + LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_2); + + // Set up timer + LL_TIM_SetCounterMode(TIM2, LL_TIM_COUNTERMODE_UP); + LL_TIM_SetClockDivision(TIM2, LL_TIM_CLOCKDIVISION_DIV1); + LL_TIM_SetPrescaler(TIM2, 0); + LL_TIM_SetAutoReload(TIM2, 10); + LL_TIM_SetCounter(TIM2, 0); + LL_TIM_EnableUpdateEvent(TIM2); + LL_TIM_EnableDMAReq_UPDATE(TIM2); + + // Start transactions + LL_TIM_GenerateEvent_UPDATE(TIM2); // Do we really need it? + LL_TIM_EnableCounter(TIM2); + + while(!LL_DMA_IsActiveFlag_TC2(DMA1)) + ; + + LL_DMA_ClearFlag_TC1(DMA1); + LL_DMA_ClearFlag_TC2(DMA1); + LL_TIM_DisableCounter(TIM2); + LL_TIM_SetCounter(TIM2, 0); + LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_1); + LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_2); + + return true; +} diff --git a/lib/digital_signal/digital_signal.h b/lib/digital_signal/digital_signal.h new file mode 100644 index 000000000..3b3ad77f7 --- /dev/null +++ b/lib/digital_signal/digital_signal.h @@ -0,0 +1,28 @@ +#pragma once + +#include +#include +#include + +#include + +typedef struct { + bool start_level; + uint32_t edge_cnt; + uint32_t edges_max_cnt; + float* edge_timings; +} DigitalSignal; + +DigitalSignal* digital_signal_alloc(uint32_t max_edges_cnt); + +void digital_signal_free(DigitalSignal* signal); + +bool digital_signal_append(DigitalSignal* signal_a, DigitalSignal* signal_b); + +bool digital_signal_get_start_level(DigitalSignal* signal); + +uint32_t digital_signal_get_edges_cnt(DigitalSignal* signal); + +float digital_signal_get_edge(DigitalSignal* signal, uint32_t edge_num); + +bool digital_signal_send(DigitalSignal* signal, const GpioPin* gpio); diff --git a/lib/lib.mk b/lib/lib.mk index cb58fadeb..43f4a1a25 100644 --- a/lib/lib.mk +++ b/lib/lib.mk @@ -95,6 +95,11 @@ C_SOURCES += $(wildcard $(LIB_DIR)/toolbox/*/*.c) CPP_SOURCES += $(wildcard $(LIB_DIR)/toolbox/*.cpp) CPP_SOURCES += $(wildcard $(LIB_DIR)/toolbox/*/*.cpp) +# Digital signal +CFLAGS += -I$(LIB_DIR)/digital_signal +C_SOURCES += $(wildcard $(LIB_DIR)/digital_signal/*.c) + + # USB Stack CFLAGS += -I$(LIB_DIR)/libusb_stm32/inc C_SOURCES += $(LIB_DIR)/libusb_stm32/src/usbd_stm32wb55_devfs.c