diff --git a/ReadMe.md b/ReadMe.md index c78b7484f47..2d91e753b49 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -68,6 +68,7 @@ This software is for experimental purposes only and is not meant for any illegal - Updated: [Air Mouse (By ginkage)](https://github.com/ginkage/FlippAirMouse/) - Updated: [Authenticator/TOTP v4 (By akopachov)](https://github.com/akopachov/flipper-zero_authenticator) - Updated: [Weather Station (By Skorpionm)-OFW](https://github.com/flipperdevices/flipperzero-good-faps/tree/dev/weather_station) [Added Support For Auriol AHFL 433B2 IPX4 sensor (By tomjschwanke)](https://github.com/flipperdevices/flipperzero-good-faps/pull/17) +- Added: [Text Viewer 2 (By Willy-JL)] Original by kowalski7cc & kyhwana, new has code borrowed from Archive-Show diff --git a/applications/external/text_viewer2/application.fam b/applications/external/text_viewer2/application.fam new file mode 100644 index 00000000000..33bcce09cb7 --- /dev/null +++ b/applications/external/text_viewer2/application.fam @@ -0,0 +1,14 @@ +App( + appid="text_viewer2", + name="Text Viewer 2", + apptype=FlipperAppType.EXTERNAL, + entry_point="text_viewer", + stack_size=10 * 1024, + fap_icon="icons/text_10px.png", + fap_icon_assets="icons", + fap_icon_assets_symbol="text_viewer", + fap_category="Tools", + fap_author="@Willy-JL", # Original by @kowalski7cc & @kyhwana, new has code borrowed from archive > show + fap_version="1.0", + fap_description="Text viewer application", +) diff --git a/applications/external/text_viewer2/icons/text_10px.png b/applications/external/text_viewer2/icons/text_10px.png new file mode 100644 index 00000000000..8e8a6183dd5 Binary files /dev/null and b/applications/external/text_viewer2/icons/text_10px.png differ diff --git a/applications/external/text_viewer2/scenes/text_viewer_scene.c b/applications/external/text_viewer2/scenes/text_viewer_scene.c new file mode 100644 index 00000000000..a507da4afe4 --- /dev/null +++ b/applications/external/text_viewer2/scenes/text_viewer_scene.c @@ -0,0 +1,30 @@ +#include "text_viewer_scene.h" + +// Generate scene on_enter handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter, +void (*const text_viewer_on_enter_handlers[])(void*) = { +#include "text_viewer_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 text_viewer_on_event_handlers[])(void* context, SceneManagerEvent event) = { +#include "text_viewer_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 text_viewer_on_exit_handlers[])(void* context) = { +#include "text_viewer_scene_config.h" +}; +#undef ADD_SCENE + +// Initialize scene handlers configuration structure +const SceneManagerHandlers text_viewer_scene_handlers = { + .on_enter_handlers = text_viewer_on_enter_handlers, + .on_event_handlers = text_viewer_on_event_handlers, + .on_exit_handlers = text_viewer_on_exit_handlers, + .scene_num = TextViewerSceneNum, +}; diff --git a/applications/external/text_viewer2/scenes/text_viewer_scene.h b/applications/external/text_viewer2/scenes/text_viewer_scene.h new file mode 100644 index 00000000000..a8770db3b95 --- /dev/null +++ b/applications/external/text_viewer2/scenes/text_viewer_scene.h @@ -0,0 +1,29 @@ +#pragma once + +#include + +// Generate scene id and total number +#define ADD_SCENE(prefix, name, id) TextViewerScene##id, +typedef enum { +#include "text_viewer_scene_config.h" + TextViewerSceneNum, +} TextViewerScene; +#undef ADD_SCENE + +extern const SceneManagerHandlers text_viewer_scene_handlers; + +// Generate scene on_enter handlers declaration +#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*); +#include "text_viewer_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 "text_viewer_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 "text_viewer_scene_config.h" +#undef ADD_SCENE diff --git a/applications/external/text_viewer2/scenes/text_viewer_scene_config.h b/applications/external/text_viewer2/scenes/text_viewer_scene_config.h new file mode 100644 index 00000000000..86c69441e9c --- /dev/null +++ b/applications/external/text_viewer2/scenes/text_viewer_scene_config.h @@ -0,0 +1 @@ +ADD_SCENE(text_viewer, show, Show) diff --git a/applications/external/text_viewer2/scenes/text_viewer_scene_show.c b/applications/external/text_viewer2/scenes/text_viewer_scene_show.c new file mode 100644 index 00000000000..3d5aa077a8f --- /dev/null +++ b/applications/external/text_viewer2/scenes/text_viewer_scene_show.c @@ -0,0 +1,132 @@ +#include "../text_viewer.h" + +#define SHOW_MAX_FILE_SIZE 8000 + +void text_viewer_scene_show_widget_callback(GuiButtonType result, InputType type, void* context) { + furi_assert(context); + TextViewer* app = (TextViewer*)context; + if(type == InputTypeShort) { + view_dispatcher_send_custom_event(app->view_dispatcher, result); + } +} + +static bool text_show_read_lines(File* file, FuriString* str_result) { + //furi_string_reset(str_result); + uint8_t buffer[SHOW_MAX_FILE_SIZE]; + + uint16_t read_count = storage_file_read(file, buffer, SHOW_MAX_FILE_SIZE); + if(storage_file_get_error(file) != FSE_OK) { + return false; + } + + for(uint16_t i = 0; i < read_count; i++) { + furi_string_push_back(str_result, buffer[i]); + } + + return true; +} + +void text_viewer_scene_show_on_enter(void* context) { + furi_assert(context); + TextViewer* app = context; + + FuriString* buffer; + buffer = furi_string_alloc(); + + Storage* storage = furi_record_open(RECORD_STORAGE); + File* file = storage_file_alloc(storage); + + FileInfo fileinfo; + FS_Error error = storage_common_stat(storage, furi_string_get_cstr(app->path), &fileinfo); + if(error == FSE_OK) { + if((fileinfo.size < SHOW_MAX_FILE_SIZE) && (fileinfo.size > 2)) { + bool ok = storage_file_open( + file, furi_string_get_cstr(app->path), FSAM_READ, FSOM_OPEN_EXISTING); + if(ok) { + if(!text_show_read_lines(file, buffer)) { + goto text_file_read_err; + } + if(!furi_string_size(buffer)) { + goto text_file_read_err; + } + + storage_file_seek(file, 0, true); + + widget_add_text_scroll_element( + app->widget, 0, 0, 128, 64, furi_string_get_cstr(buffer)); + + } else { + text_file_read_err: + widget_add_text_box_element( + app->widget, + 0, + 0, + 128, + 64, + AlignLeft, + AlignCenter, + "\e#Error:\nStorage file open error\e#", + false); + } + storage_file_close(file); + } else if(fileinfo.size < 2) { + widget_add_text_box_element( + app->widget, + 0, + 0, + 128, + 64, + AlignLeft, + AlignCenter, + "\e#Error:\nFile is too small\e#", + false); + } else { + widget_add_text_box_element( + app->widget, + 0, + 0, + 128, + 64, + AlignLeft, + AlignCenter, + "\e#Error:\nFile is too large to show\e#", + false); + } + } else { + widget_add_text_box_element( + app->widget, + 0, + 0, + 128, + 64, + AlignLeft, + AlignCenter, + "\e#Error:\nFile system error\e#", + false); + } + + furi_string_free(buffer); + + storage_file_free(file); + furi_record_close(RECORD_STORAGE); + + view_dispatcher_switch_to_view(app->view_dispatcher, TextViewerViewWidget); +} + +bool text_viewer_scene_show_on_event(void* context, SceneManagerEvent event) { + furi_assert(context); + TextViewer* app = (TextViewer*)context; + + if(event.type == SceneManagerEventTypeCustom) { + scene_manager_previous_scene(app->scene_manager); + return true; + } + return false; +} + +void text_viewer_scene_show_on_exit(void* context) { + furi_assert(context); + TextViewer* app = (TextViewer*)context; + + widget_reset(app->widget); +} diff --git a/applications/external/text_viewer2/text_viewer.c b/applications/external/text_viewer2/text_viewer.c new file mode 100644 index 00000000000..782b44c6fcf --- /dev/null +++ b/applications/external/text_viewer2/text_viewer.c @@ -0,0 +1,84 @@ +#include "text_viewer.h" + +static bool text_viewer_custom_event_callback(void* context, uint32_t event) { + furi_assert(context); + TextViewer* app = context; + return scene_manager_handle_custom_event(app->scene_manager, event); +} + +static bool text_viewer_back_event_callback(void* context) { + furi_assert(context); + TextViewer* app = context; + return scene_manager_handle_back_event(app->scene_manager); +} + +TextViewer* text_viewer_alloc() { + TextViewer* app = malloc(sizeof(TextViewer)); + app->gui = furi_record_open(RECORD_GUI); + + app->view_dispatcher = view_dispatcher_alloc(); + app->scene_manager = scene_manager_alloc(&text_viewer_scene_handlers, app); + view_dispatcher_enable_queue(app->view_dispatcher); + view_dispatcher_set_event_callback_context(app->view_dispatcher, app); + + view_dispatcher_set_custom_event_callback( + app->view_dispatcher, text_viewer_custom_event_callback); + view_dispatcher_set_navigation_event_callback( + app->view_dispatcher, text_viewer_back_event_callback); + + view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); + + app->widget = widget_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, TextViewerViewWidget, widget_get_view(app->widget)); + + app->path = furi_string_alloc(); + + return app; +} + +void text_viewer_free(TextViewer* app) { + furi_assert(app); + + view_dispatcher_remove_view(app->view_dispatcher, TextViewerViewWidget); + widget_free(app->widget); + + view_dispatcher_free(app->view_dispatcher); + scene_manager_free(app->scene_manager); + + furi_string_free(app->path); + + furi_record_close(RECORD_GUI); + free(app); +} + +extern int32_t text_viewer(void* p) { + TextViewer* app = text_viewer_alloc(); + + do { + if(p && strlen(p)) { + furi_string_set(app->path, (const char*)p); + } else { + furi_string_set(app->path, TEXT_VIEWER_PATH); + + DialogsFileBrowserOptions browser_options; + dialog_file_browser_set_basic_options( + &browser_options, TEXT_VIEWER_EXTENSION, &I_text_10px); + browser_options.hide_ext = false; + + DialogsApp* dialogs = furi_record_open(RECORD_DIALOGS); + bool res = dialog_file_browser_show(dialogs, app->path, app->path, &browser_options); + + furi_record_close(RECORD_DIALOGS); + if(!res) { + break; + } + } + + scene_manager_next_scene(app->scene_manager, TextViewerSceneShow); + view_dispatcher_run(app->view_dispatcher); + } while(false); + + text_viewer_free(app); + return 0; +} diff --git a/applications/external/text_viewer2/text_viewer.h b/applications/external/text_viewer2/text_viewer.h new file mode 100644 index 00000000000..f0427ca2834 --- /dev/null +++ b/applications/external/text_viewer2/text_viewer.h @@ -0,0 +1,26 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include "text_viewer_icons.h" +#include "scenes/text_viewer_scene.h" + +#define TEXT_VIEWER_PATH STORAGE_EXT_PATH_PREFIX +#define TEXT_VIEWER_EXTENSION "*" + +typedef struct { + Gui* gui; + SceneManager* scene_manager; + ViewDispatcher* view_dispatcher; + Widget* widget; + + FuriString* path; +} TextViewer; + +typedef enum { + TextViewerViewWidget, +} TextViewerView;