[FL-935] Queue-based SD app cycle. File select widget. (#388)
* SD App: more specific sd api * Gui: view dispatcher fix api documentation * Gui: view holder thingy * SD App: do not sleep when working with sd card bus * SD App: queue-based lifecycle * Assets: sd-card assets * SD App: init file select api Co-authored-by: あく <alleteam@gmail.com>
This commit is contained in:
		
							parent
							
								
									5866be8455
								
							
						
					
					
						commit
						cc263d743b
					
				| @ -8,13 +8,16 @@ struct FileSelect { | |||||||
|     // public
 |     // public
 | ||||||
|     View* view; |     View* view; | ||||||
|     FS_Api* fs_api; |     FS_Api* fs_api; | ||||||
|     char* path; |     const char* path; | ||||||
|     char* extension; |     const char* extension; | ||||||
| 
 | 
 | ||||||
|     bool init_completed; |     bool init_completed; | ||||||
| 
 | 
 | ||||||
|     FileSelectCallback callback; |     FileSelectCallback callback; | ||||||
|     void* context; |     void* context; | ||||||
|  | 
 | ||||||
|  |     char* buffer; | ||||||
|  |     uint8_t buffer_size; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| typedef struct { | typedef struct { | ||||||
| @ -28,7 +31,7 @@ typedef struct { | |||||||
| 
 | 
 | ||||||
| bool file_select_fill_strings(FileSelect* file_select); | bool file_select_fill_strings(FileSelect* file_select); | ||||||
| bool file_select_fill_count(FileSelect* file_select); | bool file_select_fill_count(FileSelect* file_select); | ||||||
| static bool file_select_init(FileSelect* file_select); | static bool file_select_init_inner(FileSelect* file_select); | ||||||
| 
 | 
 | ||||||
| static void file_select_draw_callback(Canvas* canvas, void* _model) { | static void file_select_draw_callback(Canvas* canvas, void* _model) { | ||||||
|     FileSelectModel* model = _model; |     FileSelectModel* model = _model; | ||||||
| @ -66,8 +69,8 @@ static bool file_select_input_callback(InputEvent* event, void* context) { | |||||||
| 
 | 
 | ||||||
|     if(event->type == InputTypeShort) { |     if(event->type == InputTypeShort) { | ||||||
|         if(!file_select->init_completed) { |         if(!file_select->init_completed) { | ||||||
|             if(!file_select_init(file_select)) { |             if(!file_select_init_inner(file_select)) { | ||||||
|                 file_select->callback(NULL, file_select->context); |                 file_select->callback(false, file_select->context); | ||||||
|             } |             } | ||||||
|         } else if(event->key == InputKeyUp) { |         } else if(event->key == InputKeyUp) { | ||||||
|             with_view_model( |             with_view_model( | ||||||
| @ -127,20 +130,24 @@ static bool file_select_input_callback(InputEvent* event, void* context) { | |||||||
|                         return false; |                         return false; | ||||||
|                     }); |                     }); | ||||||
| 
 | 
 | ||||||
|                 file_select->callback(result, file_select->context); |                 if(file_select->buffer) { | ||||||
|  |                     strlcpy(file_select->buffer, result, file_select->buffer_size); | ||||||
|  |                 }; | ||||||
|  | 
 | ||||||
|  |                 file_select->callback(true, file_select->context); | ||||||
|             } |             } | ||||||
|             consumed = true; |             consumed = true; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         if(!file_select_fill_strings(file_select)) { |         if(!file_select_fill_strings(file_select)) { | ||||||
|             file_select->callback(NULL, file_select->context); |             file_select->callback(false, file_select->context); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     return consumed; |     return consumed; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static bool file_select_init(FileSelect* file_select) { | static bool file_select_init_inner(FileSelect* file_select) { | ||||||
|     bool result = false; |     bool result = false; | ||||||
|     if(file_select->path && file_select->extension && file_select->fs_api) { |     if(file_select->path && file_select->extension && file_select->fs_api) { | ||||||
|         if(file_select_fill_count(file_select)) { |         if(file_select_fill_count(file_select)) { | ||||||
| @ -162,13 +169,6 @@ FileSelect* file_select_alloc() { | |||||||
|     view_set_draw_callback(file_select->view, file_select_draw_callback); |     view_set_draw_callback(file_select->view, file_select_draw_callback); | ||||||
|     view_set_input_callback(file_select->view, file_select_input_callback); |     view_set_input_callback(file_select->view, file_select_input_callback); | ||||||
| 
 | 
 | ||||||
|     file_select->fs_api = NULL; |  | ||||||
|     file_select->path = NULL; |  | ||||||
|     file_select->extension = NULL; |  | ||||||
|     file_select->init_completed = false; |  | ||||||
|     file_select->callback = NULL; |  | ||||||
|     file_select->context = NULL; |  | ||||||
| 
 |  | ||||||
|     with_view_model( |     with_view_model( | ||||||
|         file_select->view, (FileSelectModel * model) { |         file_select->view, (FileSelectModel * model) { | ||||||
|             for(uint8_t i = 0; i < FILENAME_COUNT; i++) { |             for(uint8_t i = 0; i < FILENAME_COUNT; i++) { | ||||||
| @ -209,12 +209,30 @@ void file_select_set_api(FileSelect* file_select, FS_Api* fs_api) { | |||||||
| void file_select_set_callback(FileSelect* file_select, FileSelectCallback callback, void* context) { | void file_select_set_callback(FileSelect* file_select, FileSelectCallback callback, void* context) { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void file_select_set_filter(FileSelect* file_select, char* path, char* extension) { | void file_select_set_filter(FileSelect* file_select, const char* path, const char* extension) { | ||||||
|     furi_assert(file_select); |     furi_assert(file_select); | ||||||
|     file_select->path = path; |     file_select->path = path; | ||||||
|     file_select->extension = extension; |     file_select->extension = extension; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | void file_select_set_result_buffer(FileSelect* file_select, char* buffer, uint8_t buffer_size) { | ||||||
|  |     file_select->buffer = buffer; | ||||||
|  |     file_select->buffer_size = buffer_size; | ||||||
|  | 
 | ||||||
|  |     if(file_select->buffer) { | ||||||
|  |         strlcpy(file_select->buffer, "", file_select->buffer_size); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool file_select_init(FileSelect* file_select) { | ||||||
|  |     if(!file_select_init_inner(file_select)) { | ||||||
|  |         file_select->callback(false, file_select->context); | ||||||
|  |         return false; | ||||||
|  |     } else { | ||||||
|  |         return true; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
| static bool filter_file(FileSelect* file_select, FileInfo* file_info, char* name) { | static bool filter_file(FileSelect* file_select, FileInfo* file_info, char* name) { | ||||||
|     bool result = false; |     bool result = false; | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -8,7 +8,7 @@ extern "C" { | |||||||
| 
 | 
 | ||||||
| typedef struct FileSelect FileSelect; | typedef struct FileSelect FileSelect; | ||||||
| 
 | 
 | ||||||
| typedef void (*FileSelectCallback)(const char* result, void* context); | typedef void (*FileSelectCallback)(bool result, void* context); | ||||||
| 
 | 
 | ||||||
| FileSelect* file_select_alloc(); | FileSelect* file_select_alloc(); | ||||||
| 
 | 
 | ||||||
| @ -17,7 +17,9 @@ View* file_select_get_view(FileSelect* file_select); | |||||||
| 
 | 
 | ||||||
| void file_select_set_api(FileSelect* file_select, FS_Api* fs_api); | void file_select_set_api(FileSelect* file_select, FS_Api* fs_api); | ||||||
| void file_select_set_callback(FileSelect* file_select, FileSelectCallback callback, void* context); | void file_select_set_callback(FileSelect* file_select, FileSelectCallback callback, void* context); | ||||||
| void file_select_set_filter(FileSelect* file_select, char* path, char* extension); | void file_select_set_filter(FileSelect* file_select, const char* path, const char* extension); | ||||||
|  | void file_select_set_result_buffer(FileSelect* file_select, char* buffer, uint8_t buffer_size); | ||||||
|  | bool file_select_init(FileSelect* file_select); | ||||||
| 
 | 
 | ||||||
| #ifdef __cplusplus | #ifdef __cplusplus | ||||||
| } | } | ||||||
|  | |||||||
| @ -7,47 +7,55 @@ | |||||||
| extern "C" { | extern "C" { | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
| /* ViewDispatcher view_port placement */ | /**
 | ||||||
|  |  * @brief ViewDispatcher view_port placement | ||||||
|  |  */ | ||||||
| typedef enum { | typedef enum { | ||||||
|     ViewDispatcherTypeNone, /* Special layer for internal use only */ |     ViewDispatcherTypeNone, /**< Special layer for internal use only */ | ||||||
|     ViewDispatcherTypeWindow, /* Main view_port layer, status bar is shown */ |     ViewDispatcherTypeWindow, /**< Main view_port layer, status bar is shown */ | ||||||
|     ViewDispatcherTypeFullscreen /* Fullscreen view_port layer */ |     ViewDispatcherTypeFullscreen /**< Fullscreen view_port layer */ | ||||||
| } ViewDispatcherType; | } ViewDispatcherType; | ||||||
| 
 | 
 | ||||||
| typedef struct ViewDispatcher ViewDispatcher; | typedef struct ViewDispatcher ViewDispatcher; | ||||||
| 
 | 
 | ||||||
| /* Allocate ViewDispatcher
 | /**
 | ||||||
|  |  * @brief Allocate ViewDispatcher | ||||||
|  * @return pointer to ViewDispatcher instance |  * @return pointer to ViewDispatcher instance | ||||||
|  */ |  */ | ||||||
| ViewDispatcher* view_dispatcher_alloc(); | ViewDispatcher* view_dispatcher_alloc(); | ||||||
| 
 | 
 | ||||||
| /* Free ViewDispatcher
 | /**
 | ||||||
|  * @param pointer to View |  * @brief Free ViewDispatcher | ||||||
|  |  * @param view_dispatcher pointer to ViewDispatcher | ||||||
|  */ |  */ | ||||||
| void view_dispatcher_free(ViewDispatcher* view_dispatcher); | void view_dispatcher_free(ViewDispatcher* view_dispatcher); | ||||||
| 
 | 
 | ||||||
| /* Add view to ViewDispatcher
 | /**
 | ||||||
|  |  * @brief Add view to ViewDispatcher | ||||||
|  * @param view_dispatcher, ViewDispatcher instance |  * @param view_dispatcher, ViewDispatcher instance | ||||||
|  * @param view_id, View id to register |  * @param view_id View id to register | ||||||
|  * @param view, View instance |  * @param view View instance | ||||||
|  */ |  */ | ||||||
| void view_dispatcher_add_view(ViewDispatcher* view_dispatcher, uint32_t view_id, View* view); | void view_dispatcher_add_view(ViewDispatcher* view_dispatcher, uint32_t view_id, View* view); | ||||||
| 
 | 
 | ||||||
| /* Remove view from ViewDispatcher
 | /**
 | ||||||
|  * @param view_dispatcher, ViewDispatcher instance |  * @brief Remove view from ViewDispatcher | ||||||
|  * @param view_id, View id to remove |  * @param view_dispatcher ViewDispatcher instance | ||||||
|  |  * @param view_id View id to remove | ||||||
|  */ |  */ | ||||||
| void view_dispatcher_remove_view(ViewDispatcher* view_dispatcher, uint32_t view_id); | void view_dispatcher_remove_view(ViewDispatcher* view_dispatcher, uint32_t view_id); | ||||||
| 
 | 
 | ||||||
| /* Switch to View
 | /**
 | ||||||
|  * @param view_dispatcher, ViewDispatcher instance |  * @brief Switch to View | ||||||
|  * @param view_id, View id to register |  * @param view_dispatcher ViewDispatcher instance | ||||||
|  |  * @param view_id View id to register | ||||||
|  */ |  */ | ||||||
| void view_dispatcher_switch_to_view(ViewDispatcher* view_dispatcher, uint32_t view_id); | void view_dispatcher_switch_to_view(ViewDispatcher* view_dispatcher, uint32_t view_id); | ||||||
| 
 | 
 | ||||||
| /* Attach ViewDispatcher to GUI
 | /**
 | ||||||
|  * @param view_dispatcher, ViewDispatcher instance |  * @brief Attach ViewDispatcher to GUI | ||||||
|  * @param gui, GUI instance to attach to |  * @param view_dispatcher ViewDispatcher instance | ||||||
|  |  * @param gui GUI instance to attach to | ||||||
|  */ |  */ | ||||||
| void view_dispatcher_attach_to_gui( | void view_dispatcher_attach_to_gui( | ||||||
|     ViewDispatcher* view_dispatcher, |     ViewDispatcher* view_dispatcher, | ||||||
|  | |||||||
| @ -27,10 +27,12 @@ bool _fs_init(SdFsInfo* _fs_info) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool _fs_lock(SdFsInfo* fs_info) { | bool _fs_lock(SdFsInfo* fs_info) { | ||||||
|  |     api_hal_power_insomnia_enter(); | ||||||
|     return (osMutexAcquire(fs_info->mutex, osWaitForever) == osOK); |     return (osMutexAcquire(fs_info->mutex, osWaitForever) == osOK); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool _fs_unlock(SdFsInfo* fs_info) { | bool _fs_unlock(SdFsInfo* fs_info) { | ||||||
|  |     api_hal_power_insomnia_exit(); | ||||||
|     return (osMutexRelease(fs_info->mutex) == osOK); |     return (osMutexRelease(fs_info->mutex) == osOK); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -6,6 +6,65 @@ | |||||||
| #include "cli/cli.h" | #include "cli/cli.h" | ||||||
| #include "api-hal-sd.h" | #include "api-hal-sd.h" | ||||||
| 
 | 
 | ||||||
|  | #include <gui/modules/dialog_ex.h> | ||||||
|  | #include <gui/modules/file_select.h> | ||||||
|  | 
 | ||||||
|  | typedef enum { | ||||||
|  |     FST_FAT12 = FS_FAT12, | ||||||
|  |     FST_FAT16 = FS_FAT16, | ||||||
|  |     FST_FAT32 = FS_FAT32, | ||||||
|  |     FST_EXFAT = FS_EXFAT, | ||||||
|  | } SDFsType; | ||||||
|  | 
 | ||||||
|  | typedef struct { | ||||||
|  |     SDFsType fs_type; | ||||||
|  |     uint32_t kb_total; | ||||||
|  |     uint32_t kb_free; | ||||||
|  |     uint16_t cluster_size; | ||||||
|  |     uint16_t sector_size; | ||||||
|  |     char label[34]; | ||||||
|  |     SDError error; | ||||||
|  | } SDInfo; | ||||||
|  | 
 | ||||||
|  | typedef enum { | ||||||
|  |     SdAppEventTypeBack, | ||||||
|  |     SdAppEventTypeOK, | ||||||
|  |     SdAppEventTypeFormat, | ||||||
|  |     SdAppEventTypeInfo, | ||||||
|  |     SdAppEventTypeEject, | ||||||
|  |     SdAppEventTypeFileSelect, | ||||||
|  |     SdAppEventTypeCheckError, | ||||||
|  | } SdAppEventType; | ||||||
|  | 
 | ||||||
|  | typedef struct { | ||||||
|  |     const char* path; | ||||||
|  |     const char* extension; | ||||||
|  |     char* result; | ||||||
|  |     uint8_t result_size; | ||||||
|  | } SdAppFileSelectData; | ||||||
|  | 
 | ||||||
|  | typedef struct { | ||||||
|  |     bool result; | ||||||
|  | } SdAppFileSelectResultEvent; | ||||||
|  | 
 | ||||||
|  | typedef struct { | ||||||
|  |     SdAppEventType type; | ||||||
|  |     osMessageQueueId_t result_receiver; | ||||||
|  |     union { | ||||||
|  |         SdAppFileSelectData file_select_data; | ||||||
|  |     } payload; | ||||||
|  | } SdAppEvent; | ||||||
|  | 
 | ||||||
|  | static void sd_icon_draw_callback(Canvas* canvas, void* context); | ||||||
|  | bool sd_api_file_select( | ||||||
|  |     SdApp* sd_app, | ||||||
|  |     const char* path, | ||||||
|  |     const char* extension, | ||||||
|  |     char* result, | ||||||
|  |     uint8_t result_size); | ||||||
|  | 
 | ||||||
|  | /******************* Allocators *******************/ | ||||||
|  | 
 | ||||||
| FS_Api* fs_api_alloc() { | FS_Api* fs_api_alloc() { | ||||||
|     FS_Api* fs_api = furi_alloc(sizeof(FS_Api)); |     FS_Api* fs_api = furi_alloc(sizeof(FS_Api)); | ||||||
| 
 | 
 | ||||||
| @ -43,77 +102,13 @@ FS_Api* fs_api_alloc() { | |||||||
|     return fs_api; |     return fs_api; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void sd_set_lines(SdApp* sd_app, uint8_t count, ...) { |  | ||||||
|     va_list argptr; |  | ||||||
|     count = min(count, SD_STATE_LINES_COUNT); |  | ||||||
| 
 |  | ||||||
|     for(uint8_t i = 0; i < SD_STATE_LINES_COUNT; i++) { |  | ||||||
|         sd_app->line[i] = ""; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     va_start(argptr, count); |  | ||||||
| 
 |  | ||||||
|     for(uint8_t i = 0; i < count; i++) { |  | ||||||
|         sd_app->line[i] = va_arg(argptr, char*); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     va_end(argptr); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void sd_icon_draw_callback(Canvas* canvas, void* context) { |  | ||||||
|     furi_assert(canvas); |  | ||||||
|     furi_assert(context); |  | ||||||
|     SdApp* sd_app = context; |  | ||||||
| 
 |  | ||||||
|     switch(sd_app->info.status) { |  | ||||||
|     case SD_NO_CARD: |  | ||||||
|         break; |  | ||||||
|     case SD_OK: |  | ||||||
|         canvas_draw_icon(canvas, 0, 0, sd_app->icon.mounted); |  | ||||||
|         break; |  | ||||||
|     default: |  | ||||||
|         canvas_draw_icon(canvas, 0, 0, sd_app->icon.fail); |  | ||||||
|         break; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void sd_app_draw_callback(Canvas* canvas, void* context) { |  | ||||||
|     furi_assert(canvas); |  | ||||||
|     furi_assert(context); |  | ||||||
|     SdApp* sd_app = context; |  | ||||||
| 
 |  | ||||||
|     canvas_clear(canvas); |  | ||||||
|     canvas_set_color(canvas, ColorBlack); |  | ||||||
|     canvas_set_font(canvas, FontPrimary); |  | ||||||
| 
 |  | ||||||
|     for(uint8_t i = 0; i < SD_STATE_LINES_COUNT; i++) { |  | ||||||
|         canvas_draw_str(canvas, 0, (i + 1) * 10, sd_app->line[i]); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void sd_app_input_callback(InputEvent* event, void* context) { |  | ||||||
|     furi_assert(context); |  | ||||||
|     SdApp* sd_app = context; |  | ||||||
| 
 |  | ||||||
|     osMessageQueuePut(sd_app->event_queue, event, 0, 0); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| SdApp* sd_app_alloc() { | SdApp* sd_app_alloc() { | ||||||
|     SdApp* sd_app = furi_alloc(sizeof(SdApp)); |     SdApp* sd_app = furi_alloc(sizeof(SdApp)); | ||||||
| 
 | 
 | ||||||
|     // init inner fs data
 |     // init inner fs data
 | ||||||
|     furi_check(_fs_init(&sd_app->info)); |     furi_check(_fs_init(&sd_app->info)); | ||||||
| 
 | 
 | ||||||
|     sd_app->event_queue = osMessageQueueNew(8, sizeof(InputEvent), NULL); |     sd_app->event_queue = osMessageQueueNew(8, sizeof(SdAppEvent), NULL); | ||||||
| 
 |  | ||||||
|     // init view_port
 |  | ||||||
|     sd_app->view_port = view_port_alloc(); |  | ||||||
|     view_port_draw_callback_set(sd_app->view_port, sd_app_draw_callback, sd_app); |  | ||||||
|     view_port_input_callback_set(sd_app->view_port, sd_app_input_callback, sd_app); |  | ||||||
|     view_port_enabled_set(sd_app->view_port, false); |  | ||||||
| 
 |  | ||||||
|     // init lines
 |  | ||||||
|     sd_set_lines(sd_app, 0); |  | ||||||
| 
 | 
 | ||||||
|     // init icon view_port
 |     // init icon view_port
 | ||||||
|     sd_app->icon.view_port = view_port_alloc(); |     sd_app->icon.view_port = view_port_alloc(); | ||||||
| @ -123,143 +118,68 @@ SdApp* sd_app_alloc() { | |||||||
|     view_port_draw_callback_set(sd_app->icon.view_port, sd_icon_draw_callback, sd_app); |     view_port_draw_callback_set(sd_app->icon.view_port, sd_icon_draw_callback, sd_app); | ||||||
|     view_port_enabled_set(sd_app->icon.view_port, false); |     view_port_enabled_set(sd_app->icon.view_port, false); | ||||||
| 
 | 
 | ||||||
|  |     // init sd card api
 | ||||||
|  |     sd_app->sd_card_api.context = sd_app; | ||||||
|  |     sd_app->sd_card_api.file_select = sd_api_file_select; | ||||||
|  |     sd_app->sd_app_state = SdAppStateBackground; | ||||||
|  |     string_init(sd_app->text_holder); | ||||||
|  | 
 | ||||||
|     return sd_app; |     return sd_app; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool app_sd_ask(SdApp* sd_app, InputKey input_true, InputKey input_false) { | /******************* Internal sd card related fns *******************/ | ||||||
|     bool result; |  | ||||||
| 
 | 
 | ||||||
|     InputEvent event; | void get_sd_info(SdApp* sd_app, SDInfo* sd_info) { | ||||||
|     while(1) { |  | ||||||
|         osStatus_t event_status = |  | ||||||
|             osMessageQueueGet(sd_app->event_queue, &event, NULL, osWaitForever); |  | ||||||
| 
 |  | ||||||
|         if(event_status == osOK) { |  | ||||||
|             if(event.type == InputTypeShort && event.key == input_true) { |  | ||||||
|                 result = true; |  | ||||||
|                 break; |  | ||||||
|             } |  | ||||||
|             if(event.type == InputTypeShort && event.key == InputKeyBack) { |  | ||||||
|                 result = false; |  | ||||||
|                 break; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     return result; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void app_sd_info_callback(void* context) { |  | ||||||
|     furi_assert(context); |  | ||||||
|     SdApp* sd_app = context; |  | ||||||
|     view_port_enabled_set(sd_app->view_port, true); |  | ||||||
| 
 |  | ||||||
|     // dynamic strings
 |  | ||||||
|     const uint8_t str_buffer_size = 26; |  | ||||||
|     const uint8_t str_count = 6; |  | ||||||
|     char* str_buffer[str_count]; |  | ||||||
|     bool memory_error = false; |  | ||||||
| 
 |  | ||||||
|     // info vars
 |  | ||||||
|     uint32_t serial_num; |  | ||||||
|     SDError get_label_result, get_free_result; |  | ||||||
|     FATFS* fs; |  | ||||||
|     uint32_t free_clusters, free_sectors, total_sectors; |     uint32_t free_clusters, free_sectors, total_sectors; | ||||||
|     char volume_label[34]; |     FATFS* fs; | ||||||
| 
 | 
 | ||||||
|     // init strings
 |     // clean data
 | ||||||
|     for(uint8_t i = 0; i < str_count; i++) { |     memset(sd_info, 0, sizeof(SDInfo)); | ||||||
|         str_buffer[i] = malloc(str_buffer_size + 1); | 
 | ||||||
|         if(str_buffer[i] == NULL) { |     // get fs info
 | ||||||
|             memory_error = true; |     _fs_lock(&sd_app->info); | ||||||
|         } else { |     sd_info->error = f_getlabel(sd_app->info.path, sd_info->label, NULL); | ||||||
|             str_buffer[i][0] = 0; |     if(sd_info->error == SD_OK) { | ||||||
|         } |         sd_info->error = f_getfree(sd_app->info.path, &free_clusters, &fs); | ||||||
|     } |     } | ||||||
|  |     _fs_unlock(&sd_app->info); | ||||||
| 
 | 
 | ||||||
|     if(memory_error) { |     if(sd_info->error == SD_OK) { | ||||||
|         sd_set_lines(sd_app, 1, "not enough memory"); |  | ||||||
|     } else { |  | ||||||
|         // get fs info
 |  | ||||||
|         _fs_lock(&sd_app->info); |  | ||||||
|         get_label_result = f_getlabel(sd_app->info.path, volume_label, &serial_num); |  | ||||||
|         get_free_result = f_getfree(sd_app->info.path, &free_clusters, &fs); |  | ||||||
|         _fs_unlock(&sd_app->info); |  | ||||||
| 
 |  | ||||||
|         // calculate size
 |         // calculate size
 | ||||||
|         total_sectors = (fs->n_fatent - 2) * fs->csize; |         total_sectors = (fs->n_fatent - 2) * fs->csize; | ||||||
|         free_sectors = free_clusters * fs->csize; |         free_sectors = free_clusters * fs->csize; | ||||||
|  | 
 | ||||||
|         uint16_t sector_size = _MAX_SS; |         uint16_t sector_size = _MAX_SS; | ||||||
| #if _MAX_SS != _MIN_SS | #if _MAX_SS != _MIN_SS | ||||||
|         sector_size = fs->ssize; |         sector_size = fs->ssize; | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
|         // output info to dynamic strings
 |         sd_info->fs_type = fs->fs_type; | ||||||
|         if(get_label_result == SD_OK && get_free_result == SD_OK) { |  | ||||||
|             snprintf(str_buffer[0], str_buffer_size, "%s", volume_label); |  | ||||||
| 
 | 
 | ||||||
|             const char* fs_type = ""; |         sd_info->kb_total = total_sectors / 1024 * sector_size; | ||||||
| 
 |         sd_info->kb_free = free_sectors / 1024 * sector_size; | ||||||
|             switch(fs->fs_type) { |         sd_info->cluster_size = fs->csize; | ||||||
|             case(FS_FAT12): |         sd_info->sector_size = sector_size; | ||||||
|                 fs_type = "FAT12"; |  | ||||||
|                 break; |  | ||||||
|             case(FS_FAT16): |  | ||||||
|                 fs_type = "FAT16"; |  | ||||||
|                 break; |  | ||||||
|             case(FS_FAT32): |  | ||||||
|                 fs_type = "FAT32"; |  | ||||||
|                 break; |  | ||||||
|             case(FS_EXFAT): |  | ||||||
|                 fs_type = "EXFAT"; |  | ||||||
|                 break; |  | ||||||
|             default: |  | ||||||
|                 fs_type = "UNKNOWN"; |  | ||||||
|                 break; |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             snprintf(str_buffer[1], str_buffer_size, "%s, S/N: %lu", fs_type, serial_num); |  | ||||||
| 
 |  | ||||||
|             snprintf(str_buffer[2], str_buffer_size, "Cluster: %d sectors", fs->csize); |  | ||||||
|             snprintf(str_buffer[3], str_buffer_size, "Sector: %d bytes", sector_size); |  | ||||||
|             snprintf( |  | ||||||
|                 str_buffer[4], str_buffer_size, "%lu KB total", total_sectors / 1024 * sector_size); |  | ||||||
|             snprintf( |  | ||||||
|                 str_buffer[5], str_buffer_size, "%lu KB free", free_sectors / 1024 * sector_size); |  | ||||||
|         } else { |  | ||||||
|             snprintf(str_buffer[0], str_buffer_size, "SD status error:"); |  | ||||||
|             snprintf( |  | ||||||
|                 str_buffer[1], |  | ||||||
|                 str_buffer_size, |  | ||||||
|                 "%s", |  | ||||||
|                 fs_error_get_internal_desc(_fs_status(&sd_app->info))); |  | ||||||
|             snprintf(str_buffer[2], str_buffer_size, "Label error:"); |  | ||||||
|             snprintf( |  | ||||||
|                 str_buffer[3], str_buffer_size, "%s", fs_error_get_internal_desc(get_label_result)); |  | ||||||
|             snprintf(str_buffer[4], str_buffer_size, "Get free error:"); |  | ||||||
|             snprintf( |  | ||||||
|                 str_buffer[5], str_buffer_size, "%s", fs_error_get_internal_desc(get_free_result)); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         // dynamic strings to screen
 |  | ||||||
|         sd_set_lines( |  | ||||||
|             sd_app, |  | ||||||
|             6, |  | ||||||
|             str_buffer[0], |  | ||||||
|             str_buffer[1], |  | ||||||
|             str_buffer[2], |  | ||||||
|             str_buffer[3], |  | ||||||
|             str_buffer[4], |  | ||||||
|             str_buffer[5]); |  | ||||||
|     } |     } | ||||||
|  | } | ||||||
| 
 | 
 | ||||||
|     app_sd_ask(sd_app, InputKeyBack, InputKeyBack); | const char* get_fs_type_text(SDFsType fs_type) { | ||||||
| 
 |     switch(fs_type) { | ||||||
|     sd_set_lines(sd_app, 0); |     case(FST_FAT12): | ||||||
|     view_port_enabled_set(sd_app->view_port, false); |         return "FAT12"; | ||||||
| 
 |         break; | ||||||
|     for(uint8_t i = 0; i < str_count; i++) { |     case(FST_FAT16): | ||||||
|         free(str_buffer[i]); |         return "FAT16"; | ||||||
|  |         break; | ||||||
|  |     case(FST_FAT32): | ||||||
|  |         return "FAT32"; | ||||||
|  |         break; | ||||||
|  |     case(FST_EXFAT): | ||||||
|  |         return "EXFAT"; | ||||||
|  |         break; | ||||||
|  |     default: | ||||||
|  |         return "UNKNOWN"; | ||||||
|  |         break; | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -284,39 +204,6 @@ void app_sd_format_internal(SdApp* sd_app) { | |||||||
|     _fs_unlock(&sd_app->info); |     _fs_unlock(&sd_app->info); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void app_sd_format_callback(void* context) { |  | ||||||
|     furi_assert(context); |  | ||||||
|     SdApp* sd_app = context; |  | ||||||
| 
 |  | ||||||
|     // ask to really format
 |  | ||||||
|     sd_set_lines(sd_app, 2, "Press UP to format", "or BACK to exit"); |  | ||||||
|     view_port_enabled_set(sd_app->view_port, true); |  | ||||||
| 
 |  | ||||||
|     // wait for input
 |  | ||||||
|     if(!app_sd_ask(sd_app, InputKeyUp, InputKeyBack)) { |  | ||||||
|         view_port_enabled_set(sd_app->view_port, false); |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     // show warning
 |  | ||||||
|     sd_set_lines(sd_app, 3, "formatting SD card", "procedure can be lengthy", "please wait"); |  | ||||||
| 
 |  | ||||||
|     // format card
 |  | ||||||
|     app_sd_format_internal(sd_app); |  | ||||||
| 
 |  | ||||||
|     if(sd_app->info.status != SD_OK) { |  | ||||||
|         sd_set_lines( |  | ||||||
|             sd_app, 2, "SD card format error", fs_error_get_internal_desc(sd_app->info.status)); |  | ||||||
|     } else { |  | ||||||
|         sd_set_lines(sd_app, 1, "SD card formatted"); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     // wait for BACK
 |  | ||||||
|     app_sd_ask(sd_app, InputKeyBack, InputKeyBack); |  | ||||||
| 
 |  | ||||||
|     view_port_enabled_set(sd_app->view_port, false); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void app_sd_notify_wait_on() { | void app_sd_notify_wait_on() { | ||||||
|     api_hal_light_set(LightRed, 0xFF); |     api_hal_light_set(LightRed, 0xFF); | ||||||
|     api_hal_light_set(LightBlue, 0xFF); |     api_hal_light_set(LightBlue, 0xFF); | ||||||
| @ -433,23 +320,149 @@ void app_sd_unmount_card(SdApp* sd_app) { | |||||||
|     _fs_unlock(&sd_app->info); |     _fs_unlock(&sd_app->info); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void app_sd_eject_callback(void* context) { | bool app_sd_make_path(const char* path) { | ||||||
|  |     furi_assert(path); | ||||||
|  | 
 | ||||||
|  |     if(*path) { | ||||||
|  |         char* file_path = strdup(path); | ||||||
|  | 
 | ||||||
|  |         for(char* p = strchr(file_path + 1, '/'); p; p = strchr(p + 1, '/')) { | ||||||
|  |             *p = '\0'; | ||||||
|  |             SDError result = f_mkdir(file_path); | ||||||
|  | 
 | ||||||
|  |             if(result != SD_OK) { | ||||||
|  |                 if(result != SD_EXIST) { | ||||||
|  |                     *p = '/'; | ||||||
|  |                     free(file_path); | ||||||
|  |                     return false; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             *p = '/'; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         free(file_path); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /******************* Draw callbacks *******************/ | ||||||
|  | 
 | ||||||
|  | static void sd_icon_draw_callback(Canvas* canvas, void* context) { | ||||||
|  |     furi_assert(canvas); | ||||||
|     furi_assert(context); |     furi_assert(context); | ||||||
|     SdApp* sd_app = context; |     SdApp* sd_app = context; | ||||||
| 
 | 
 | ||||||
|     sd_set_lines(sd_app, 1, "ejecting SD card"); |     switch(sd_app->info.status) { | ||||||
|     view_port_enabled_set(sd_app->view_port, true); |     case SD_NO_CARD: | ||||||
| 
 |         break; | ||||||
|     app_sd_unmount_card(sd_app); |     case SD_OK: | ||||||
| 
 |         canvas_draw_icon(canvas, 0, 0, sd_app->icon.mounted); | ||||||
|     sd_set_lines(sd_app, 1, "SD card can be pulled out"); |         break; | ||||||
| 
 |     default: | ||||||
|     // wait for BACK
 |         canvas_draw_icon(canvas, 0, 0, sd_app->icon.fail); | ||||||
|     app_sd_ask(sd_app, InputKeyBack, InputKeyBack); |         break; | ||||||
| 
 |     } | ||||||
|     view_port_enabled_set(sd_app->view_port, false); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | /******************* SD-api callbacks *******************/ | ||||||
|  | 
 | ||||||
|  | bool sd_api_file_select( | ||||||
|  |     SdApp* sd_app, | ||||||
|  |     const char* path, | ||||||
|  |     const char* extension, | ||||||
|  |     char* result, | ||||||
|  |     uint8_t result_size) { | ||||||
|  |     bool retval = false; | ||||||
|  | 
 | ||||||
|  |     osMessageQueueId_t return_event_queue = | ||||||
|  |         osMessageQueueNew(1, sizeof(SdAppFileSelectResultEvent), NULL); | ||||||
|  | 
 | ||||||
|  |     SdAppEvent message = { | ||||||
|  |         .type = SdAppEventTypeFileSelect, | ||||||
|  |         .result_receiver = return_event_queue, | ||||||
|  |         .payload = { | ||||||
|  |             .file_select_data = { | ||||||
|  |                 .path = path, | ||||||
|  |                 .extension = extension, | ||||||
|  |                 .result = result, | ||||||
|  |                 .result_size = result_size}}}; | ||||||
|  | 
 | ||||||
|  |     furi_check(osMessageQueuePut(sd_app->event_queue, &message, 0, osWaitForever) == osOK); | ||||||
|  | 
 | ||||||
|  |     SdAppFileSelectResultEvent event; | ||||||
|  |     while(1) { | ||||||
|  |         osStatus_t event_status = | ||||||
|  |             osMessageQueueGet(sd_app->event_queue, &event, NULL, osWaitForever); | ||||||
|  |         if(event_status == osOK) { | ||||||
|  |             retval = event.result; | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return retval; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /******************* View callbacks *******************/ | ||||||
|  | 
 | ||||||
|  | void app_view_back_callback(void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     SdApp* sd_app = context; | ||||||
|  |     SdAppEvent message = {.type = SdAppEventTypeBack}; | ||||||
|  |     furi_check(osMessageQueuePut(sd_app->event_queue, &message, 0, osWaitForever) == osOK); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void app_view_dialog_callback(DialogExResult result, void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     SdApp* sd_app = context; | ||||||
|  | 
 | ||||||
|  |     if(result == DialogExResultLeft) { | ||||||
|  |         SdAppEvent message = {.type = SdAppEventTypeBack}; | ||||||
|  |         furi_check(osMessageQueuePut(sd_app->event_queue, &message, 0, osWaitForever) == osOK); | ||||||
|  |     } else if(result == DialogExResultRight) { | ||||||
|  |         SdAppEvent message = {.type = SdAppEventTypeOK}; | ||||||
|  |         furi_check(osMessageQueuePut(sd_app->event_queue, &message, 0, osWaitForever) == osOK); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void app_view_file_select_callback(bool result, void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     SdApp* sd_app = context; | ||||||
|  | 
 | ||||||
|  |     if(result) { | ||||||
|  |         SdAppEvent message = {.type = SdAppEventTypeOK}; | ||||||
|  |         furi_check(osMessageQueuePut(sd_app->event_queue, &message, 0, osWaitForever) == osOK); | ||||||
|  |     } else { | ||||||
|  |         SdAppEvent message = {.type = SdAppEventTypeBack}; | ||||||
|  |         furi_check(osMessageQueuePut(sd_app->event_queue, &message, 0, osWaitForever) == osOK); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /******************* Menu callbacks *******************/ | ||||||
|  | 
 | ||||||
|  | void app_sd_info_callback(void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     SdApp* sd_app = context; | ||||||
|  |     SdAppEvent message = {.type = SdAppEventTypeInfo}; | ||||||
|  |     furi_check(osMessageQueuePut(sd_app->event_queue, &message, 0, osWaitForever) == osOK); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void app_sd_format_callback(void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     SdApp* sd_app = context; | ||||||
|  |     SdAppEvent message = {.type = SdAppEventTypeFormat}; | ||||||
|  |     furi_check(osMessageQueuePut(sd_app->event_queue, &message, 0, osWaitForever) == osOK); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void app_sd_eject_callback(void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     SdApp* sd_app = context; | ||||||
|  |     SdAppEvent message = {.type = SdAppEventTypeEject}; | ||||||
|  |     furi_check(osMessageQueuePut(sd_app->event_queue, &message, 0, osWaitForever) == osOK); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /******************* Cli callbacks *******************/ | ||||||
|  | 
 | ||||||
| static void cli_sd_status(string_t args, void* _ctx) { | static void cli_sd_status(string_t args, void* _ctx) { | ||||||
|     SdApp* sd_app = (SdApp*)_ctx; |     SdApp* sd_app = (SdApp*)_ctx; | ||||||
| 
 | 
 | ||||||
| @ -477,93 +490,81 @@ static void cli_sd_format(string_t args, void* _ctx) { | |||||||
| 
 | 
 | ||||||
| static void cli_sd_info(string_t args, void* _ctx) { | static void cli_sd_info(string_t args, void* _ctx) { | ||||||
|     SdApp* sd_app = (SdApp*)_ctx; |     SdApp* sd_app = (SdApp*)_ctx; | ||||||
|  |     SDInfo sd_info; | ||||||
| 
 | 
 | ||||||
|     const uint8_t str_buffer_size = 64; |     get_sd_info(sd_app, &sd_info); | ||||||
|     char str_buffer[str_buffer_size]; |  | ||||||
| 
 | 
 | ||||||
|     // info vars
 |     if(sd_info.error == SD_OK) { | ||||||
|     uint32_t serial_num; |         const char* fs_type = get_fs_type_text(sd_info.fs_type); | ||||||
|     SDError get_label_result, get_free_result; |         printf("Label: %s\r\n", sd_info.label); | ||||||
|     FATFS* fs; |         printf("%s\r\n", fs_type); | ||||||
|     uint32_t free_clusters, free_sectors, total_sectors; |         printf("Cluster: %d sectors\r\n", sd_info.cluster_size); | ||||||
|     char volume_label[34]; |         printf("Sector: %d bytes\r\n", sd_info.sector_size); | ||||||
| 
 |         printf("%lu KB total\r\n", sd_info.kb_total); | ||||||
|     // get fs info
 |         printf("%lu KB free\r\n", sd_info.kb_free); | ||||||
|     _fs_lock(&sd_app->info); |  | ||||||
|     get_label_result = f_getlabel(sd_app->info.path, volume_label, &serial_num); |  | ||||||
|     get_free_result = f_getfree(sd_app->info.path, &free_clusters, &fs); |  | ||||||
|     _fs_unlock(&sd_app->info); |  | ||||||
| 
 |  | ||||||
|     // calculate size
 |  | ||||||
|     total_sectors = (fs->n_fatent - 2) * fs->csize; |  | ||||||
|     free_sectors = free_clusters * fs->csize; |  | ||||||
|     uint16_t sector_size = _MAX_SS; |  | ||||||
| #if _MAX_SS != _MIN_SS |  | ||||||
|     sector_size = fs->ssize; |  | ||||||
| #endif |  | ||||||
| 
 |  | ||||||
|     // output info to dynamic strings
 |  | ||||||
|     if(get_label_result == SD_OK && get_free_result == SD_OK) { |  | ||||||
|         const char* fs_type = ""; |  | ||||||
| 
 |  | ||||||
|         switch(fs->fs_type) { |  | ||||||
|         case(FS_FAT12): |  | ||||||
|             fs_type = "FAT12"; |  | ||||||
|             break; |  | ||||||
|         case(FS_FAT16): |  | ||||||
|             fs_type = "FAT16"; |  | ||||||
|             break; |  | ||||||
|         case(FS_FAT32): |  | ||||||
|             fs_type = "FAT32"; |  | ||||||
|             break; |  | ||||||
|         case(FS_EXFAT): |  | ||||||
|             fs_type = "EXFAT"; |  | ||||||
|             break; |  | ||||||
|         default: |  | ||||||
|             fs_type = "UNKNOWN"; |  | ||||||
|             break; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         snprintf(str_buffer, str_buffer_size, "Label: %s\r\n", volume_label); |  | ||||||
|         printf(str_buffer); |  | ||||||
| 
 |  | ||||||
|         snprintf(str_buffer, str_buffer_size, "%s, S/N: %lu\r\n", fs_type, serial_num); |  | ||||||
|         printf(str_buffer); |  | ||||||
| 
 |  | ||||||
|         snprintf(str_buffer, str_buffer_size, "Cluster: %d sectors\r\n", fs->csize); |  | ||||||
|         printf(str_buffer); |  | ||||||
| 
 |  | ||||||
|         snprintf(str_buffer, str_buffer_size, "Sector: %d bytes\r\n", sector_size); |  | ||||||
|         printf(str_buffer); |  | ||||||
| 
 |  | ||||||
|         snprintf( |  | ||||||
|             str_buffer, str_buffer_size, "%lu KB total\r\n", total_sectors / 1024 * sector_size); |  | ||||||
|         printf(str_buffer); |  | ||||||
| 
 |  | ||||||
|         snprintf( |  | ||||||
|             str_buffer, str_buffer_size, "%lu KB free\r\n", free_sectors / 1024 * sector_size); |  | ||||||
|         printf(str_buffer); |  | ||||||
|     } else { |     } else { | ||||||
|         printf("SD status error: "); |         printf("SD status error: %s\r\n", fs_error_get_internal_desc(_fs_status(&sd_app->info))); | ||||||
|         snprintf( |         printf("SD info error: %s\r\n", fs_error_get_internal_desc(sd_info.error)); | ||||||
|             str_buffer, |  | ||||||
|             str_buffer_size, |  | ||||||
|             "%s\r\n", |  | ||||||
|             fs_error_get_internal_desc(_fs_status(&sd_app->info))); |  | ||||||
|         printf(str_buffer); |  | ||||||
| 
 |  | ||||||
|         printf("Label error: "); |  | ||||||
|         snprintf( |  | ||||||
|             str_buffer, str_buffer_size, "%s\r\n", fs_error_get_internal_desc(get_label_result)); |  | ||||||
|         printf(str_buffer); |  | ||||||
| 
 |  | ||||||
|         printf("Get free error: "); |  | ||||||
|         snprintf( |  | ||||||
|             str_buffer, str_buffer_size, "%s\r\n", fs_error_get_internal_desc(get_free_result)); |  | ||||||
|         printf(str_buffer); |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | /******************* Test *******************/ | ||||||
|  | 
 | ||||||
|  | bool try_to_alloc_view_holder(SdApp* sd_app, Gui* gui) { | ||||||
|  |     bool result = false; | ||||||
|  | 
 | ||||||
|  |     _fs_lock(&sd_app->info); | ||||||
|  | 
 | ||||||
|  |     if(sd_app->view_holder == NULL) { | ||||||
|  |         sd_app->view_holder = view_holder_alloc(); | ||||||
|  |         view_holder_attach_to_gui(sd_app->view_holder, gui); | ||||||
|  |         view_holder_set_back_callback(sd_app->view_holder, app_view_back_callback, sd_app); | ||||||
|  |         result = true; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     _fs_unlock(&sd_app->info); | ||||||
|  | 
 | ||||||
|  |     return result; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | DialogEx* alloc_and_attach_dialog(SdApp* sd_app) { | ||||||
|  |     DialogEx* dialog = dialog_ex_alloc(); | ||||||
|  |     dialog_ex_set_context(dialog, sd_app); | ||||||
|  |     dialog_ex_set_result_callback(dialog, app_view_dialog_callback); | ||||||
|  |     view_holder_set_view(sd_app->view_holder, dialog_ex_get_view(dialog)); | ||||||
|  |     view_holder_set_free_callback(sd_app->view_holder, (FreeCallback)dialog_ex_free, dialog); | ||||||
|  |     return dialog; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | FileSelect* alloc_and_attach_file_select(SdApp* sd_app) { | ||||||
|  |     FileSelect* file_select = file_select_alloc(); | ||||||
|  |     file_select_set_callback(file_select, app_view_file_select_callback, sd_app); | ||||||
|  |     view_holder_set_view(sd_app->view_holder, file_select_get_view(file_select)); | ||||||
|  |     view_holder_set_free_callback( | ||||||
|  |         sd_app->view_holder, (FreeCallback)file_select_free, file_select); | ||||||
|  |     return file_select; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void free_view_holder(SdApp* sd_app) { | ||||||
|  |     _fs_lock(&sd_app->info); | ||||||
|  | 
 | ||||||
|  |     if(sd_app->view_holder) { | ||||||
|  |         view_holder_free(sd_app->view_holder); | ||||||
|  |         sd_app->view_holder = NULL; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     _fs_unlock(&sd_app->info); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void app_reset_state(SdApp* sd_app) { | ||||||
|  |     view_holder_stop(sd_app->view_holder); | ||||||
|  |     free_view_holder(sd_app); | ||||||
|  |     string_set_str(sd_app->text_holder, ""); | ||||||
|  |     sd_app->sd_app_state = SdAppStateBackground; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /******************* Main app *******************/ | ||||||
|  | 
 | ||||||
| int32_t sd_filesystem(void* p) { | int32_t sd_filesystem(void* p) { | ||||||
|     SdApp* sd_app = sd_app_alloc(); |     SdApp* sd_app = sd_app_alloc(); | ||||||
|     FS_Api* fs_api = fs_api_alloc(); |     FS_Api* fs_api = fs_api_alloc(); | ||||||
| @ -572,7 +573,6 @@ int32_t sd_filesystem(void* p) { | |||||||
|     Cli* cli = furi_record_open("cli"); |     Cli* cli = furi_record_open("cli"); | ||||||
|     ValueMutex* menu_vm = furi_record_open("menu"); |     ValueMutex* menu_vm = furi_record_open("menu"); | ||||||
| 
 | 
 | ||||||
|     gui_add_view_port(gui, sd_app->view_port, GuiLayerFullscreen); |  | ||||||
|     gui_add_view_port(gui, sd_app->icon.view_port, GuiLayerStatusBarLeft); |     gui_add_view_port(gui, sd_app->icon.view_port, GuiLayerStatusBarLeft); | ||||||
| 
 | 
 | ||||||
|     cli_add_command(cli, "sd_status", cli_sd_status, sd_app); |     cli_add_command(cli, "sd_status", cli_sd_status, sd_app); | ||||||
| @ -603,6 +603,7 @@ int32_t sd_filesystem(void* p) { | |||||||
| 
 | 
 | ||||||
|     // add api record
 |     // add api record
 | ||||||
|     furi_record_create("sdcard", fs_api); |     furi_record_create("sdcard", fs_api); | ||||||
|  |     furi_record_create("sdcard-ex", &sd_app->sd_card_api); | ||||||
| 
 | 
 | ||||||
|     // sd card cycle
 |     // sd card cycle
 | ||||||
|     bool sd_was_present = true; |     bool sd_was_present = true; | ||||||
| @ -648,7 +649,184 @@ int32_t sd_filesystem(void* p) { | |||||||
|             } |             } | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         delay(1000); |         SdAppEvent event; | ||||||
|  |         osStatus_t event_status = osMessageQueueGet(sd_app->event_queue, &event, NULL, 1000); | ||||||
|  | 
 | ||||||
|  |         const uint8_t y_1_line = 32; | ||||||
|  |         const uint8_t y_2_line = 32; | ||||||
|  |         const uint8_t y_4_line = 26; | ||||||
|  | 
 | ||||||
|  |         if(event_status == osOK) { | ||||||
|  |             switch(event.type) { | ||||||
|  |             case SdAppEventTypeOK: | ||||||
|  |                 switch(sd_app->sd_app_state) { | ||||||
|  |                 case SdAppStateFormat: { | ||||||
|  |                     DialogEx* dialog = view_holder_get_free_context(sd_app->view_holder); | ||||||
|  |                     dialog_ex_set_left_button_text(dialog, NULL); | ||||||
|  |                     dialog_ex_set_right_button_text(dialog, NULL); | ||||||
|  |                     dialog_ex_set_header( | ||||||
|  |                         dialog, "Formatting...", 64, y_1_line, AlignCenter, AlignCenter); | ||||||
|  |                     dialog_ex_set_text(dialog, NULL, 0, 0, AlignCenter, AlignCenter); | ||||||
|  |                     sd_app->sd_app_state = SdAppStateFormatInProgress; | ||||||
|  |                     delay(100); | ||||||
|  |                     app_sd_format_internal(sd_app); | ||||||
|  |                     app_sd_notify_success(); | ||||||
|  |                     dialog_ex_set_left_button_text(dialog, "Back"); | ||||||
|  |                     dialog_ex_set_header( | ||||||
|  |                         dialog, "SD card formatted", 64, 10, AlignCenter, AlignCenter); | ||||||
|  |                     dialog_ex_set_text( | ||||||
|  |                         dialog, "Press back to return", 64, y_1_line, AlignCenter, AlignCenter); | ||||||
|  |                     sd_app->sd_app_state = SdAppStateFormatCompleted; | ||||||
|  |                 }; break; | ||||||
|  |                 case SdAppStateEject: { | ||||||
|  |                     DialogEx* dialog = view_holder_get_free_context(sd_app->view_holder); | ||||||
|  |                     dialog_ex_set_right_button_text(dialog, NULL); | ||||||
|  |                     dialog_ex_set_header( | ||||||
|  |                         dialog, "SD card ejected", 64, 10, AlignCenter, AlignCenter); | ||||||
|  |                     dialog_ex_set_text( | ||||||
|  |                         dialog, | ||||||
|  |                         "Now the SD card\ncan be removed.", | ||||||
|  |                         64, | ||||||
|  |                         y_2_line, | ||||||
|  |                         AlignCenter, | ||||||
|  |                         AlignCenter); | ||||||
|  |                     sd_app->sd_app_state = SdAppStateEjected; | ||||||
|  |                     app_sd_unmount_card(sd_app); | ||||||
|  |                     app_sd_notify_eject(); | ||||||
|  |                 }; break; | ||||||
|  |                 case SdAppStateFileSelect: { | ||||||
|  |                     SdAppFileSelectResultEvent retval = {.result = true}; | ||||||
|  |                     furi_check( | ||||||
|  |                         osMessageQueuePut(event.result_receiver, &retval, 0, osWaitForever) == | ||||||
|  |                         osOK); | ||||||
|  |                     app_reset_state(sd_app); | ||||||
|  |                 }; break; | ||||||
|  |                 default: | ||||||
|  |                     break; | ||||||
|  |                 } | ||||||
|  |                 break; | ||||||
|  |             case SdAppEventTypeBack: | ||||||
|  |                 switch(sd_app->sd_app_state) { | ||||||
|  |                 case SdAppStateFormatInProgress: | ||||||
|  |                     break; | ||||||
|  |                 case SdAppStateFileSelect: { | ||||||
|  |                     SdAppFileSelectResultEvent retval = {.result = false}; | ||||||
|  |                     furi_check( | ||||||
|  |                         osMessageQueuePut(event.result_receiver, &retval, 0, osWaitForever) == | ||||||
|  |                         osOK); | ||||||
|  |                     app_reset_state(sd_app); | ||||||
|  |                 }; break; | ||||||
|  | 
 | ||||||
|  |                 default: | ||||||
|  |                     app_reset_state(sd_app); | ||||||
|  |                     break; | ||||||
|  |                 } | ||||||
|  |                 break; | ||||||
|  |             case SdAppEventTypeFormat: | ||||||
|  |                 if(try_to_alloc_view_holder(sd_app, gui)) { | ||||||
|  |                     DialogEx* dialog = alloc_and_attach_dialog(sd_app); | ||||||
|  |                     dialog_ex_set_left_button_text(dialog, "Back"); | ||||||
|  |                     dialog_ex_set_right_button_text(dialog, "Format"); | ||||||
|  |                     dialog_ex_set_header( | ||||||
|  |                         dialog, "Format SD card?", 64, 10, AlignCenter, AlignCenter); | ||||||
|  |                     dialog_ex_set_text( | ||||||
|  |                         dialog, "All data will be lost.", 64, y_1_line, AlignCenter, AlignCenter); | ||||||
|  |                     view_holder_start(sd_app->view_holder); | ||||||
|  |                     sd_app->sd_app_state = SdAppStateFormat; | ||||||
|  |                 } | ||||||
|  |                 break; | ||||||
|  |             case SdAppEventTypeInfo: | ||||||
|  |                 if(try_to_alloc_view_holder(sd_app, gui)) { | ||||||
|  |                     DialogEx* dialog = alloc_and_attach_dialog(sd_app); | ||||||
|  |                     dialog_ex_set_left_button_text(dialog, "Back"); | ||||||
|  | 
 | ||||||
|  |                     SDInfo sd_info; | ||||||
|  |                     get_sd_info(sd_app, &sd_info); | ||||||
|  | 
 | ||||||
|  |                     if(sd_info.error == SD_OK) { | ||||||
|  |                         string_printf( | ||||||
|  |                             sd_app->text_holder, | ||||||
|  |                             "Label: %s\nType: %s\n%lu KB total\n%lu KB free", | ||||||
|  |                             sd_info.label, | ||||||
|  |                             get_fs_type_text(sd_info.fs_type), | ||||||
|  |                             sd_info.kb_total, | ||||||
|  |                             sd_info.kb_free); | ||||||
|  |                         dialog_ex_set_text( | ||||||
|  |                             dialog, | ||||||
|  |                             string_get_cstr(sd_app->text_holder), | ||||||
|  |                             4, | ||||||
|  |                             y_4_line, | ||||||
|  |                             AlignLeft, | ||||||
|  |                             AlignCenter); | ||||||
|  |                         view_holder_start(sd_app->view_holder); | ||||||
|  |                     } else { | ||||||
|  |                         string_printf( | ||||||
|  |                             sd_app->text_holder, | ||||||
|  |                             "SD status: %s\n SD info: %s", | ||||||
|  |                             fs_error_get_internal_desc(_fs_status(&sd_app->info)), | ||||||
|  |                             fs_error_get_internal_desc(sd_info.error)); | ||||||
|  |                         dialog_ex_set_header(dialog, "Error", 64, 10, AlignCenter, AlignCenter); | ||||||
|  |                         dialog_ex_set_text( | ||||||
|  |                             dialog, | ||||||
|  |                             string_get_cstr(sd_app->text_holder), | ||||||
|  |                             64, | ||||||
|  |                             y_2_line, | ||||||
|  |                             AlignCenter, | ||||||
|  |                             AlignCenter); | ||||||
|  |                         view_holder_start(sd_app->view_holder); | ||||||
|  |                     } | ||||||
|  | 
 | ||||||
|  |                     sd_app->sd_app_state = SdAppStateInfo; | ||||||
|  |                 } | ||||||
|  |                 break; | ||||||
|  |             case SdAppEventTypeEject: | ||||||
|  |                 if(try_to_alloc_view_holder(sd_app, gui)) { | ||||||
|  |                     DialogEx* dialog = alloc_and_attach_dialog(sd_app); | ||||||
|  |                     dialog_ex_set_left_button_text(dialog, "Back"); | ||||||
|  |                     dialog_ex_set_right_button_text(dialog, "Eject"); | ||||||
|  |                     dialog_ex_set_header( | ||||||
|  |                         dialog, "Eject SD card?", 64, 10, AlignCenter, AlignCenter); | ||||||
|  |                     dialog_ex_set_text( | ||||||
|  |                         dialog, | ||||||
|  |                         "SD card will be\nunavailable", | ||||||
|  |                         64, | ||||||
|  |                         y_2_line, | ||||||
|  |                         AlignCenter, | ||||||
|  |                         AlignCenter); | ||||||
|  |                     view_holder_start(sd_app->view_holder); | ||||||
|  |                     sd_app->sd_app_state = SdAppStateEject; | ||||||
|  |                 } | ||||||
|  |                 break; | ||||||
|  |             case SdAppEventTypeFileSelect: | ||||||
|  |                 if(!app_sd_make_path(event.payload.file_select_data.path)) { | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 if(try_to_alloc_view_holder(sd_app, gui)) { | ||||||
|  |                     sd_app->result_receiver = event.result_receiver; | ||||||
|  |                     FileSelect* file_select = alloc_and_attach_file_select(sd_app); | ||||||
|  |                     file_select_set_api(file_select, fs_api); | ||||||
|  |                     file_select_set_filter( | ||||||
|  |                         file_select, | ||||||
|  |                         event.payload.file_select_data.path, | ||||||
|  |                         event.payload.file_select_data.extension); | ||||||
|  |                     file_select_set_result_buffer( | ||||||
|  |                         file_select, | ||||||
|  |                         event.payload.file_select_data.result, | ||||||
|  |                         event.payload.file_select_data.result_size); | ||||||
|  |                     if(!file_select_init(file_select)) { | ||||||
|  |                     } | ||||||
|  |                     sd_app->sd_app_state = SdAppStateFileSelect; | ||||||
|  |                 } else { | ||||||
|  |                     SdAppFileSelectResultEvent retval = {.result = false}; | ||||||
|  |                     furi_check( | ||||||
|  |                         osMessageQueuePut(event.result_receiver, &retval, 0, osWaitForever) == | ||||||
|  |                         osOK); | ||||||
|  |                 } | ||||||
|  |                 break; | ||||||
|  |             case SdAppEventTypeCheckError: | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     return 0; |     return 0; | ||||||
|  | |||||||
| @ -4,6 +4,9 @@ | |||||||
| #include <api-hal.h> | #include <api-hal.h> | ||||||
| #include <gui/gui.h> | #include <gui/gui.h> | ||||||
| #include <input/input.h> | #include <input/input.h> | ||||||
|  | #include <m-string.h> | ||||||
|  | #include "sd-card-api.h" | ||||||
|  | #include "view_holder.h" | ||||||
| 
 | 
 | ||||||
| #define SD_FS_MAX_FILES _FS_LOCK | #define SD_FS_MAX_FILES _FS_LOCK | ||||||
| #define SD_STATE_LINES_COUNT 6 | #define SD_STATE_LINES_COUNT 6 | ||||||
| @ -77,14 +80,30 @@ typedef struct { | |||||||
|     FATFS fat_fs; |     FATFS fat_fs; | ||||||
| } SdFsInfo; | } SdFsInfo; | ||||||
| 
 | 
 | ||||||
| typedef struct { | typedef enum { | ||||||
|  |     SdAppStateBackground, | ||||||
|  |     SdAppStateFormat, | ||||||
|  |     SdAppStateFormatInProgress, | ||||||
|  |     SdAppStateFormatCompleted, | ||||||
|  |     SdAppStateInfo, | ||||||
|  |     SdAppStateEject, | ||||||
|  |     SdAppStateEjected, | ||||||
|  |     SdAppStateFileSelect, | ||||||
|  | } SdAppState; | ||||||
|  | 
 | ||||||
|  | struct SdApp { | ||||||
|     SdFsInfo info; |     SdFsInfo info; | ||||||
|     SdFsIcon icon; |     SdFsIcon icon; | ||||||
| 
 | 
 | ||||||
|     ViewPort* view_port; |     SdCard_Api sd_card_api; | ||||||
|     const char* line[SD_STATE_LINES_COUNT]; |     SdAppState sd_app_state; | ||||||
|  | 
 | ||||||
|  |     ViewHolder* view_holder; | ||||||
|  |     osMessageQueueId_t result_receiver; | ||||||
|  | 
 | ||||||
|     osMessageQueueId_t event_queue; |     osMessageQueueId_t event_queue; | ||||||
| } SdApp; |     string_t text_holder; | ||||||
|  | }; | ||||||
| 
 | 
 | ||||||
| /* core api fns */ | /* core api fns */ | ||||||
| bool _fs_init(SdFsInfo* _fs_info); | bool _fs_init(SdFsInfo* _fs_info); | ||||||
|  | |||||||
							
								
								
									
										110
									
								
								applications/sd-filesystem/view_holder.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										110
									
								
								applications/sd-filesystem/view_holder.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,110 @@ | |||||||
|  | #include "view_holder.h" | ||||||
|  | #include <gui/view_i.h> | ||||||
|  | 
 | ||||||
|  | struct ViewHolder { | ||||||
|  |     View* view; | ||||||
|  |     ViewPort* view_port; | ||||||
|  |     Gui* gui; | ||||||
|  | 
 | ||||||
|  |     FreeCallback free_callback; | ||||||
|  |     void* free_context; | ||||||
|  | 
 | ||||||
|  |     BackCallback back_callback; | ||||||
|  |     void* back_context; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static void view_holder_draw_callback(Canvas* canvas, void* context); | ||||||
|  | static void view_holder_input_callback(InputEvent* event, void* context); | ||||||
|  | 
 | ||||||
|  | ViewHolder* view_holder_alloc() { | ||||||
|  |     ViewHolder* view_holder = furi_alloc(sizeof(ViewHolder)); | ||||||
|  | 
 | ||||||
|  |     view_holder->view_port = view_port_alloc(); | ||||||
|  |     view_port_draw_callback_set(view_holder->view_port, view_holder_draw_callback, view_holder); | ||||||
|  |     view_port_input_callback_set(view_holder->view_port, view_holder_input_callback, view_holder); | ||||||
|  |     view_port_enabled_set(view_holder->view_port, false); | ||||||
|  | 
 | ||||||
|  |     return view_holder; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void view_holder_free(ViewHolder* view_holder) { | ||||||
|  |     furi_assert(view_holder); | ||||||
|  | 
 | ||||||
|  |     if(view_holder->gui) { | ||||||
|  |         gui_remove_view_port(view_holder->gui, view_holder->view_port); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     view_port_free(view_holder->view_port); | ||||||
|  | 
 | ||||||
|  |     if(view_holder->free_callback) { | ||||||
|  |         view_holder->free_callback(view_holder->free_context); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     free(view_holder); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void view_holder_set_view(ViewHolder* view_holder, View* view) { | ||||||
|  |     furi_assert(view_holder); | ||||||
|  |     view_holder->view = view; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void view_holder_set_free_callback( | ||||||
|  |     ViewHolder* view_holder, | ||||||
|  |     FreeCallback free_callback, | ||||||
|  |     void* free_context) { | ||||||
|  |     furi_assert(view_holder); | ||||||
|  |     view_holder->free_callback = free_callback; | ||||||
|  |     view_holder->free_context = free_context; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void* view_holder_get_free_context(ViewHolder* view_holder) { | ||||||
|  |     return view_holder->free_context; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void view_holder_set_back_callback( | ||||||
|  |     ViewHolder* view_holder, | ||||||
|  |     BackCallback back_callback, | ||||||
|  |     void* back_context) { | ||||||
|  |     furi_assert(view_holder); | ||||||
|  |     view_holder->back_callback = back_callback; | ||||||
|  |     view_holder->back_context = back_context; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void view_holder_attach_to_gui(ViewHolder* view_holder, Gui* gui) { | ||||||
|  |     furi_assert(gui); | ||||||
|  |     furi_assert(view_holder); | ||||||
|  |     view_holder->gui = gui; | ||||||
|  |     gui_add_view_port(gui, view_holder->view_port, GuiLayerFullscreen); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void view_holder_start(ViewHolder* view_holder) { | ||||||
|  |     view_port_enabled_set(view_holder->view_port, true); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void view_holder_stop(ViewHolder* view_holder) { | ||||||
|  |     view_port_enabled_set(view_holder->view_port, false); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void view_holder_draw_callback(Canvas* canvas, void* context) { | ||||||
|  |     ViewHolder* view_holder = context; | ||||||
|  |     if(view_holder->view) { | ||||||
|  |         view_draw(view_holder->view, canvas); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void view_holder_input_callback(InputEvent* event, void* context) { | ||||||
|  |     ViewHolder* view_holder = context; | ||||||
|  |     bool is_consumed = false; | ||||||
|  | 
 | ||||||
|  |     if(view_holder->view) { | ||||||
|  |         is_consumed = view_input(view_holder->view, event); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if(!is_consumed && event->type == InputTypeShort) { | ||||||
|  |         if(event->key == InputKeyBack) { | ||||||
|  |             if(view_holder->back_callback) { | ||||||
|  |                 view_holder->back_callback(view_holder->back_context); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										92
									
								
								applications/sd-filesystem/view_holder.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										92
									
								
								applications/sd-filesystem/view_holder.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,92 @@ | |||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <gui/view.h> | ||||||
|  | #include <gui/gui.h> | ||||||
|  | 
 | ||||||
|  | #ifdef __cplusplus | ||||||
|  | extern "C" { | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | typedef struct ViewHolder ViewHolder; | ||||||
|  | 
 | ||||||
|  | /** 
 | ||||||
|  |  * @brief Free callback type | ||||||
|  |  */ | ||||||
|  | typedef void (*FreeCallback)(void* free_context); | ||||||
|  | 
 | ||||||
|  | /** 
 | ||||||
|  |  * @brief Back callback type | ||||||
|  |  * @warning comes from GUI thread | ||||||
|  |  */ | ||||||
|  | typedef void (*BackCallback)(void* back_context); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * @brief Allocate ViewHolder | ||||||
|  |  * @return pointer to ViewHolder instance | ||||||
|  |  */ | ||||||
|  | ViewHolder* view_holder_alloc(); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * @brief Free ViewHolder and call Free callback | ||||||
|  |  * @param view_holder pointer to ViewHolder | ||||||
|  |  */ | ||||||
|  | void view_holder_free(ViewHolder* view_holder); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * @brief Set view for ViewHolder | ||||||
|  |  *  | ||||||
|  |  * @param view_holder ViewHolder instance | ||||||
|  |  * @param view View instance | ||||||
|  |  */ | ||||||
|  | void view_holder_set_view(ViewHolder* view_holder, View* view); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * @brief Set Free callback | ||||||
|  |  *  | ||||||
|  |  * @param view_holder ViewHolder instance | ||||||
|  |  * @param free_callback callback pointer | ||||||
|  |  * @param free_context callback context | ||||||
|  |  */ | ||||||
|  | void view_holder_set_free_callback( | ||||||
|  |     ViewHolder* view_holder, | ||||||
|  |     FreeCallback free_callback, | ||||||
|  |     void* free_context); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * @brief Free callback context getter. Useful if your Free callback is a module destructor, so you can get an instance of the module using this method. | ||||||
|  |  *  | ||||||
|  |  * @param view_holder ViewHolder instance | ||||||
|  |  * @return void* free callback context | ||||||
|  |  */ | ||||||
|  | void* view_holder_get_free_context(ViewHolder* view_holder); | ||||||
|  | 
 | ||||||
|  | void view_holder_set_back_callback( | ||||||
|  |     ViewHolder* view_holder, | ||||||
|  |     BackCallback back_callback, | ||||||
|  |     void* back_context); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * @brief Attach ViewHolder to GUI | ||||||
|  |  *  | ||||||
|  |  * @param view_holder ViewHolder instance | ||||||
|  |  * @param gui GUI instance to attach to | ||||||
|  |  */ | ||||||
|  | void view_holder_attach_to_gui(ViewHolder* view_holder, Gui* gui); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * @brief Enable view processing | ||||||
|  |  *  | ||||||
|  |  * @param view_holder  | ||||||
|  |  */ | ||||||
|  | void view_holder_start(ViewHolder* view_holder); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * @brief Disable view processing | ||||||
|  |  *  | ||||||
|  |  * @param view_holder  | ||||||
|  |  */ | ||||||
|  | void view_holder_stop(ViewHolder* view_holder); | ||||||
|  | 
 | ||||||
|  | #ifdef __cplusplus | ||||||
|  | } | ||||||
|  | #endif | ||||||
							
								
								
									
										
											BIN
										
									
								
								assets/icons/SDCard/SDError_43x35.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								assets/icons/SDCard/SDError_43x35.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 1.8 KiB | 
							
								
								
									
										
											BIN
										
									
								
								assets/icons/SDCard/SDQuestion_35x43.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								assets/icons/SDCard/SDQuestion_35x43.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 1.9 KiB | 
							
								
								
									
										22
									
								
								lib/common-api/sd-card-api.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								lib/common-api/sd-card-api.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,22 @@ | |||||||
|  | #pragma once | ||||||
|  | #include <furi.h> | ||||||
|  | 
 | ||||||
|  | #ifdef __cplusplus | ||||||
|  | extern "C" { | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | typedef struct SdApp SdApp; | ||||||
|  | 
 | ||||||
|  | typedef struct { | ||||||
|  |     SdApp* context; | ||||||
|  |     bool (*file_select)( | ||||||
|  |         SdApp* context, | ||||||
|  |         const char* path, | ||||||
|  |         const char* extension, | ||||||
|  |         char* result, | ||||||
|  |         uint8_t result_size); | ||||||
|  | } SdCard_Api; | ||||||
|  | 
 | ||||||
|  | #ifdef __cplusplus | ||||||
|  | } | ||||||
|  | #endif | ||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 SG
						SG