From 9d23602968a9a0e361465398e4c82821b9143623 Mon Sep 17 00:00:00 2001 From: its your bedtime <23366927+itsyourbedtime@users.noreply.github.com> Date: Fri, 25 Jun 2021 19:57:42 +0300 Subject: [PATCH] [FL-1477] Archive improvements (#541) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * various small issues fixes * proper fadding to favourites * cleanups * fix infrared extension * allow subdirs in app tabs * fix elements_scrollbar args * bugfixes, looping list, halfbaked pinning * pinning * ui updates Co-authored-by: あく --- applications/archive/archive.c | 168 ++++++++++++++++++++------- applications/archive/archive_i.h | 29 +++-- applications/archive/archive_views.c | 61 ++++++---- applications/archive/archive_views.h | 21 +++- applications/gui/elements.c | 2 +- applications/gui/elements.h | 2 +- 6 files changed, 201 insertions(+), 82 deletions(-) diff --git a/applications/archive/archive.c b/applications/archive/archive.c index 07d6cbfa..313fe153 100644 --- a/applications/archive/archive.c +++ b/applications/archive/archive.c @@ -2,6 +2,21 @@ static bool archive_get_filenames(ArchiveApp* archive); +static bool is_favourite(ArchiveApp* archive, ArchiveFile_t* file) { + FS_Common_Api* common_api = &archive->fs_api->common; + FileInfo file_info; + FS_Error fr; + string_t path; + + string_init_set(path, "favourites/"); + string_cat(path, file->name); + + fr = common_api->info(string_get_cstr(path), &file_info, NULL, 0); + FURI_LOG_I("FAV", "%d", fr); + + return fr == 0 || fr == 2; +} + static void update_offset(ArchiveApp* archive) { furi_assert(archive); @@ -9,10 +24,13 @@ static void update_offset(ArchiveApp* archive) { archive->view_archive_main, (ArchiveViewModel * model) { size_t array_size = files_array_size(model->files); uint16_t bounds = array_size > 3 ? 2 : array_size; - if(model->list_offset < model->idx - bounds) { - model->list_offset = CLAMP(model->list_offset + 1, array_size - (bounds + 2), 0); + + if(array_size > 3 && model->idx >= array_size - 1) { + model->list_offset = model->idx - 3; + } else if(model->list_offset < model->idx - bounds) { + model->list_offset = CLAMP(model->list_offset + 1, array_size - bounds, 0); } else if(model->list_offset > model->idx - bounds) { - model->list_offset = CLAMP(model->idx - 1, array_size - (bounds), 0); + model->list_offset = CLAMP(model->idx - 1, array_size - bounds, 0); } return true; }); @@ -33,9 +51,9 @@ static void archive_update_last_idx(ArchiveApp* archive) { static void archive_switch_dir(ArchiveApp* archive, const char* path) { furi_assert(archive); furi_assert(path); - string_set(archive->browser.path, path); archive_get_filenames(archive); + update_offset(archive); } static void archive_switch_tab(ArchiveApp* archive) { @@ -45,22 +63,22 @@ static void archive_switch_tab(ArchiveApp* archive) { archive->view_archive_main, (ArchiveViewModel * model) { model->tab_idx = archive->browser.tab_id; model->idx = 0; + return true; }); archive->browser.depth = 0; archive_switch_dir(archive, tab_default_paths[archive->browser.tab_id]); - - update_offset(archive); } static void archive_leave_dir(ArchiveApp* archive) { furi_assert(archive); - size_t last_char = - string_search_rchar(archive->browser.path, '/', string_size(archive->browser.path)); - if(last_char) { - string_right(archive->browser.path, last_char); + char* last_char_ptr = strrchr(string_get_cstr(archive->browser.path), '/'); + + if(last_char_ptr) { + size_t pos = last_char_ptr - string_get_cstr(archive->browser.path); + string_left(archive->browser.path, pos); } archive->browser.depth = CLAMP(archive->browser.depth - 1, MAX_DEPTH, 0); @@ -68,12 +86,13 @@ static void archive_leave_dir(ArchiveApp* archive) { with_view_model( archive->view_archive_main, (ArchiveViewModel * model) { model->idx = archive->browser.last_idx[archive->browser.depth]; + model->list_offset = + model->idx - + (files_array_size(model->files) > 3 ? 3 : files_array_size(model->files)); return true; }); archive_switch_dir(archive, string_get_cstr(archive->browser.path)); - - update_offset(archive); } static void archive_enter_dir(ArchiveApp* archive, string_t name) { @@ -87,8 +106,6 @@ static void archive_enter_dir(ArchiveApp* archive, string_t name) { string_cat(archive->browser.path, archive->browser.name); archive_switch_dir(archive, string_get_cstr(archive->browser.path)); - - update_offset(archive); } static bool filter_by_extension(ArchiveApp* archive, FileInfo* file_info, const char* name) { @@ -103,6 +120,8 @@ static bool filter_by_extension(ArchiveApp* archive, FileInfo* file_info, const result = true; } else if(strstr(name, filter_ext_ptr) != NULL) { result = true; + } else if(file_info->flags & FSF_DIRECTORY) { + result = true; } return result; @@ -208,6 +227,16 @@ static void archive_add_to_favourites(ArchiveApp* archive) { furi_assert(archive); FS_Common_Api* common_api = &archive->fs_api->common; + common_api->mkdir("favourites"); + + FS_File_Api* file_api = &archive->fs_api->file; + File src; + File dst; + + bool fr; + uint16_t buffer[MAX_FILE_SIZE]; + uint16_t bw = 0; + uint16_t br = 0; string_t buffer_src; string_t buffer_dst; @@ -219,7 +248,20 @@ static void archive_add_to_favourites(ArchiveApp* archive) { string_init_set_str(buffer_dst, "/favourites/"); string_cat(buffer_dst, archive->browser.name); - common_api->rename(string_get_cstr(buffer_src), string_get_cstr(buffer_dst)); + fr = file_api->open(&src, string_get_cstr(buffer_src), FSAM_READ, FSOM_OPEN_EXISTING); + FURI_LOG_I("FATFS", "OPEN: %d", fr); + fr = file_api->open(&dst, string_get_cstr(buffer_dst), FSAM_WRITE, FSOM_CREATE_ALWAYS); + FURI_LOG_I("FATFS", "CREATE: %d", fr); + + for(;;) { + br = file_api->read(&src, &buffer, sizeof(buffer)); + if(br == 0) break; + bw = file_api->write(&dst, &buffer, sizeof(buffer)); + if(bw < br) break; + } + + file_api->close(&src); + file_api->close(&dst); string_clear(buffer_src); string_clear(buffer_dst); @@ -295,8 +337,12 @@ static void archive_show_file_menu(ArchiveApp* archive) { with_view_model( archive->view_archive_main, (ArchiveViewModel * model) { + ArchiveFile_t* selected; + selected = files_array_get(model->files, model->idx); model->menu = true; model->menu_idx = 0; + selected->fav = is_favourite(archive, selected); + return true; }); } @@ -321,29 +367,43 @@ static void archive_open_app(ArchiveApp* archive, const char* app_name, const ch app_loader_start(app_name, args); } -static void archive_delete_file(ArchiveApp* archive, string_t name) { +static void archive_delete_file(ArchiveApp* archive, ArchiveFile_t* file, bool fav, bool orig) { furi_assert(archive); - furi_assert(name); + furi_assert(file); FS_Common_Api* common_api = &archive->fs_api->common; - string_t path; - string_init_set(path, archive->browser.path); - string_cat(path, "/"); - string_cat(path, name); + string_init(path); + + if(!fav && !orig) { + string_set(path, archive->browser.path); + string_cat(path, "/"); + string_cat(path, file->name); + common_api->remove(string_get_cstr(path)); + + } else { // remove from favorites + string_set(path, "favourites/"); + string_cat(path, file->name); + common_api->remove(string_get_cstr(path)); + + if(orig) { // remove original file + string_set_str(path, get_default_path(file->type)); + string_cat(path, "/"); + string_cat(path, file->name); + common_api->remove(string_get_cstr(path)); + } + } - common_api->remove(string_get_cstr(path)); string_clear(path); - archive_get_filenames(archive); - update_offset(archive); - with_view_model( archive->view_archive_main, (ArchiveViewModel * model) { model->idx = CLAMP(model->idx, files_array_size(model->files) - 1, 0); return true; }); + + update_offset(archive); } static void archive_file_menu_callback(ArchiveApp* archive) { @@ -361,7 +421,7 @@ static void archive_file_menu_callback(ArchiveApp* archive) { switch(idx) { case 0: - if((selected->type != ArchiveFileTypeFolder && selected->type != ArchiveFileTypeUnknown)) { + if(is_known_app(selected->type)) { string_t full_path; string_init_set(full_path, archive->browser.path); string_cat(full_path, "/"); @@ -374,19 +434,34 @@ static void archive_file_menu_callback(ArchiveApp* archive) { } break; case 1: - - string_set(archive->browser.name, selected->name); - archive_add_to_favourites(archive); - archive_close_file_menu(archive); + if(is_known_app(selected->type)) { + if(!is_favourite(archive, selected)) { + string_set(archive->browser.name, selected->name); + archive_add_to_favourites(archive); + } else { + // delete from favourites + archive_delete_file(archive, selected, true, false); + } + archive_close_file_menu(archive); + } break; case 2: // open rename view - archive_enter_text_input(archive); + if(is_known_app(selected->type)) { + archive_enter_text_input(archive); + } break; case 3: // confirmation? - archive_delete_file(archive, selected->name); + if(is_favourite(archive, selected)) { + //delete both fav & original + archive_delete_file(archive, selected, true, true); + } else { + archive_delete_file(archive, selected, false, false); + } + archive_close_file_menu(archive); + break; default: @@ -405,9 +480,9 @@ static void menu_input_handler(ArchiveApp* archive, InputEvent* event) { with_view_model( archive->view_archive_main, (ArchiveViewModel * model) { if(event->key == InputKeyUp) { - model->menu_idx = CLAMP(model->menu_idx - 1, MENU_ITEMS - 1, 0); + model->menu_idx = ((model->menu_idx - 1) + MENU_ITEMS) % MENU_ITEMS; } else if(event->key == InputKeyDown) { - model->menu_idx = CLAMP(model->menu_idx + 1, MENU_ITEMS - 1, 0); + model->menu_idx = (model->menu_idx + 1) % MENU_ITEMS; } return true; }); @@ -437,14 +512,18 @@ static bool archive_view_input(InputEvent* event, void* context) { if(event->type == InputTypeShort) { if(event->key == InputKeyLeft) { - archive->browser.tab_id = CLAMP(archive->browser.tab_id - 1, ArchiveTabTotal, 0); - archive_switch_tab(archive); - return true; - + if(archive->browser.tab_id > 0) { + archive->browser.tab_id = CLAMP(archive->browser.tab_id - 1, ArchiveTabTotal, 0); + archive_switch_tab(archive); + return true; + } } else if(event->key == InputKeyRight) { - archive->browser.tab_id = CLAMP(archive->browser.tab_id + 1, ArchiveTabTotal - 1, 0); - archive_switch_tab(archive); - return true; + if(archive->browser.tab_id < ArchiveTabTotal - 1) { + archive->browser.tab_id = + CLAMP(archive->browser.tab_id + 1, ArchiveTabTotal - 1, 0); + archive_switch_tab(archive); + return true; + } } else if(event->key == InputKeyBack) { if(archive->browser.depth == 0) { @@ -459,14 +538,15 @@ static bool archive_view_input(InputEvent* event, void* context) { if(event->key == InputKeyUp || event->key == InputKeyDown) { with_view_model( archive->view_archive_main, (ArchiveViewModel * model) { - size_t num_elements = files_array_size(model->files) - 1; + uint16_t num_elements = (uint16_t)files_array_size(model->files); if((event->type == InputTypeShort || event->type == InputTypeRepeat)) { if(event->key == InputKeyUp) { - model->idx = CLAMP(model->idx - 1, num_elements, 0); + model->idx = ((model->idx - 1) + num_elements) % num_elements; } else if(event->key == InputKeyDown) { - model->idx = CLAMP(model->idx + 1, num_elements, 0); + model->idx = (model->idx + 1) % num_elements; } } + return true; }); update_offset(archive); diff --git a/applications/archive/archive_i.h b/applications/archive/archive_i.h index e91ddb5a..bed81e9a 100644 --- a/applications/archive/archive_i.h +++ b/applications/archive/archive_i.h @@ -16,6 +16,7 @@ #define MAX_DEPTH 32 #define MAX_FILES 100 //temp +#define MAX_FILE_SIZE 128 typedef enum { ArchiveViewMain, @@ -23,17 +24,6 @@ typedef enum { ArchiveViewTotal, } ArchiveViewEnum; -typedef enum { - ArchiveTabFavourites, - ArchiveTabIButton, - ArchiveTabNFC, - ArchiveTabSubOne, - ArchiveTabLFRFID, - ArchiveTabIrda, - ArchiveTabBrowser, - ArchiveTabTotal, -} ArchiveTabEnum; - static const char* flipper_app_name[] = { [ArchiveFileTypeIButton] = "iButton", [ArchiveFileTypeNFC] = "NFC", @@ -77,6 +67,23 @@ static inline const char* get_tab_ext(ArchiveTabEnum tab) { } } +static inline const char* get_default_path(ArchiveFileTypeEnum type) { + switch(type) { + case ArchiveFileTypeIButton: + return tab_default_paths[ArchiveTabIButton]; + case ArchiveFileTypeNFC: + return tab_default_paths[ArchiveTabNFC]; + case ArchiveFileTypeSubOne: + return tab_default_paths[ArchiveTabSubOne]; + case ArchiveFileTypeLFRFID: + return tab_default_paths[ArchiveTabLFRFID]; + case ArchiveFileTypeIrda: + return tab_default_paths[ArchiveTabIrda]; + default: + return false; + } +} + typedef enum { EventTypeTick, EventTypeKey, diff --git a/applications/archive/archive_views.c b/applications/archive/archive_views.c index 7845698e..7663dfd7 100644 --- a/applications/archive/archive_views.c +++ b/applications/archive/archive_views.c @@ -1,7 +1,13 @@ #include "archive_views.h" -static const char* ArchiveTabNames[] = - {"Favourites", "iButton", "NFC", "SubOne", "Rfid", "Infrared", "Browser"}; +static const char* ArchiveTabNames[] = { + [ArchiveTabFavourites] = "Favourites", + [ArchiveTabIButton] = "iButton", + [ArchiveTabNFC] = "NFC", + [ArchiveTabSubOne] = "SubOne", + [ArchiveTabLFRFID] = "RFID LF", + [ArchiveTabIrda] = "Infrared", + [ArchiveTabBrowser] = "Browser"}; static const IconName ArchiveItemIcons[] = { [ArchiveFileTypeIButton] = I_ibutt_10px, @@ -13,19 +19,15 @@ static const IconName ArchiveItemIcons[] = { [ArchiveFileTypeUnknown] = I_unknown_10px, }; -static inline bool is_known_app(ArchiveFileTypeEnum type) { - return (type != ArchiveFileTypeFolder && type != ArchiveFileTypeUnknown); -} - static void render_item_menu(Canvas* canvas, ArchiveViewModel* model) { canvas_set_color(canvas, ColorWhite); - canvas_draw_box(canvas, 61, 17, 62, 46); + canvas_draw_box(canvas, 71, 17, 57, 46); canvas_set_color(canvas, ColorBlack); - elements_slightly_rounded_frame(canvas, 60, 16, 64, 48); + elements_slightly_rounded_frame(canvas, 70, 16, 58, 48); string_t menu[MENU_ITEMS]; - string_init_set_str(menu[0], "Open in app"); + string_init_set_str(menu[0], "Run in app"); string_init_set_str(menu[1], "Pin"); string_init_set_str(menu[2], "Rename"); string_init_set_str(menu[3], "Delete"); @@ -35,16 +37,17 @@ static void render_item_menu(Canvas* canvas, ArchiveViewModel* model) { if(!is_known_app(selected->type)) { string_set_str(menu[0], "---"); string_set_str(menu[1], "---"); - } else if(model->tab_idx == 0) { - string_set_str(menu[1], "Move"); + string_set_str(menu[2], "---"); + } else if(model->tab_idx == 0 || selected->fav) { + string_set_str(menu[1], "Unpin"); } for(size_t i = 0; i < MENU_ITEMS; i++) { - canvas_draw_str(canvas, 72, 27 + i * 11, string_get_cstr(menu[i])); + canvas_draw_str(canvas, 82, 27 + i * 11, string_get_cstr(menu[i])); string_clear(menu[i]); } - canvas_draw_icon_name(canvas, 64, 20 + model->menu_idx * 11, I_ButtonRight_4x7); + canvas_draw_icon_name(canvas, 74, 20 + model->menu_idx * 11, I_ButtonRight_4x7); } void archive_trim_file_ext(char* name) { @@ -82,14 +85,18 @@ static void draw_list(Canvas* canvas, ArchiveViewModel* model) { char cstr_buff[MAX_NAME_LEN]; string_init(str_buff); - for(size_t i = 0; i < MIN(MENU_ITEMS, array_size); ++i) { + for(size_t i = 0; i < MIN(array_size, MENU_ITEMS); ++i) { size_t idx = CLAMP(i + model->list_offset, array_size, 0); ArchiveFile_t* file = files_array_get(model->files, CLAMP(idx, array_size - 1, 0)); - strlcpy(cstr_buff, string_get_cstr(file->name), string_size(file->name)); + strlcpy(cstr_buff, string_get_cstr(file->name), string_size(file->name) + 1); if(is_known_app(file->type)) archive_trim_file_ext(cstr_buff); string_set_str(str_buff, cstr_buff); + if(is_known_app(file->type)) { + archive_trim_file_ext(cstr_buff); + } + elements_string_fit_width(canvas, str_buff, scrollbar ? MAX_LEN_PX - 6 : MAX_LEN_PX); if(model->idx == idx) { @@ -104,7 +111,7 @@ static void draw_list(Canvas* canvas, ArchiveViewModel* model) { } if(scrollbar) { - elements_scrollbar_pos(canvas, 126, 16, 48, model->idx, array_size); + elements_scrollbar_pos(canvas, 126, 15, 49, model->idx, array_size); } if(model->menu) { @@ -123,20 +130,30 @@ static void archive_render_status_bar(Canvas* canvas, ArchiveViewModel* model) { canvas_set_color(canvas, ColorWhite); canvas_draw_box(canvas, 0, 0, 50, 13); - canvas_draw_box(canvas, 100, 0, 28, 13); + canvas_draw_box(canvas, 107, 0, 20, 13); canvas_set_color(canvas, ColorBlack); - elements_frame(canvas, 0, 0, 50, 13); - canvas_draw_str_aligned(canvas, 25, 10, AlignCenter, AlignBottom, tab_name); + canvas_draw_frame(canvas, 1, 0, 50, 12); + canvas_draw_line(canvas, 0, 1, 0, 11); + canvas_draw_line(canvas, 1, 12, 49, 12); + canvas_draw_str_aligned(canvas, 26, 9, AlignCenter, AlignBottom, tab_name); - elements_frame(canvas, 100, 0, 24, 13); + canvas_draw_frame(canvas, 108, 0, 20, 12); + canvas_draw_line(canvas, 107, 1, 107, 11); + canvas_draw_line(canvas, 108, 12, 126, 12); if(model->tab_idx > 0) { - canvas_draw_icon_name(canvas, 106, 3, I_ButtonLeft_4x7); + canvas_draw_icon_name(canvas, 112, 2, I_ButtonLeft_4x7); } if(model->tab_idx < SIZEOF_ARRAY(ArchiveTabNames) - 1) { - canvas_draw_icon_name(canvas, 114, 3, I_ButtonRight_4x7); + canvas_draw_icon_name(canvas, 120, 2, I_ButtonRight_4x7); } + + canvas_set_color(canvas, ColorWhite); + canvas_draw_dot(canvas, 50, 0); + canvas_draw_dot(canvas, 127, 0); + + canvas_set_color(canvas, ColorBlack); } void archive_view_render(Canvas* canvas, void* model) { diff --git a/applications/archive/archive_views.h b/applications/archive/archive_views.h index eabd0be0..a291161b 100644 --- a/applications/archive/archive_views.h +++ b/applications/archive/archive_views.h @@ -6,9 +6,8 @@ #include #include -#define MAX_LEN_PX 98 +#define MAX_LEN_PX 100 #define MAX_NAME_LEN 255 - #define FRAME_HEIGHT 12 #define MENU_ITEMS 4 @@ -23,9 +22,21 @@ typedef enum { AppIdTotal, } ArchiveFileTypeEnum; +typedef enum { + ArchiveTabFavourites, + ArchiveTabLFRFID, + ArchiveTabSubOne, + ArchiveTabNFC, + ArchiveTabIButton, + ArchiveTabIrda, + ArchiveTabBrowser, + ArchiveTabTotal, +} ArchiveTabEnum; + typedef struct { string_t name; ArchiveFileTypeEnum type; + bool fav; } ArchiveFile_t; static void ArchiveFile_t_init(ArchiveFile_t* obj) { @@ -65,4 +76,8 @@ typedef struct { } ArchiveViewModel; void archive_view_render(Canvas* canvas, void* model); -void archive_trim_file_ext(char* name); \ No newline at end of file +void archive_trim_file_ext(char* name); + +static inline bool is_known_app(ArchiveFileTypeEnum type) { + return (type != ArchiveFileTypeFolder && type != ArchiveFileTypeUnknown); +} diff --git a/applications/gui/elements.c b/applications/gui/elements.c index 182c88f6..8217b6ef 100644 --- a/applications/gui/elements.c +++ b/applications/gui/elements.c @@ -67,7 +67,7 @@ void elements_scrollbar_pos( } } -void elements_scrollbar(Canvas* canvas, uint8_t pos, uint8_t total) { +void elements_scrollbar(Canvas* canvas, uint16_t pos, uint16_t total) { furi_assert(canvas); uint8_t width = canvas_width(canvas); diff --git a/applications/gui/elements.h b/applications/gui/elements.h index 6257f26a..151cbb9a 100644 --- a/applications/gui/elements.h +++ b/applications/gui/elements.h @@ -46,7 +46,7 @@ void elements_scrollbar_pos( * @param pos - current element of total elements * @param total - total elements */ -void elements_scrollbar(Canvas* canvas, uint8_t pos, uint8_t total); +void elements_scrollbar(Canvas* canvas, uint16_t pos, uint16_t total); /* * Draw rounded frame