From 8ff9c9de26cb6178753438d1eef2c5d4b06e5d6a Mon Sep 17 00:00:00 2001 From: Derek Jamison Date: Sun, 29 Sep 2024 13:55:33 -0600 Subject: [PATCH] Initial check-in for gemini app --- README.md | 12 ++- application.fam | 12 +++ gemini.png | Bin 0 -> 145 bytes gemini_app.c | 53 +++++++++ gemini_app.h | 6 ++ gemini_app_i.h | 18 ++++ scenes/gemini_scene.c | 30 ++++++ scenes/gemini_scene.h | 29 +++++ scenes/gemini_scene_config.h | 2 + scenes/gemini_scene_main_menu.c | 130 +++++++++++++++++++++++ scenes/gemini_scene_under_construction.c | 19 ++++ 11 files changed, 307 insertions(+), 4 deletions(-) create mode 100644 application.fam create mode 100644 gemini.png create mode 100644 gemini_app.c create mode 100644 gemini_app.h create mode 100644 gemini_app_i.h create mode 100644 scenes/gemini_scene.c create mode 100644 scenes/gemini_scene.h create mode 100644 scenes/gemini_scene_config.h create mode 100644 scenes/gemini_scene_main_menu.c create mode 100644 scenes/gemini_scene_under_construction.c diff --git a/README.md b/README.md index 65b09c6a296..172ed8d6366 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ Currently in development stage, here I'm going to upload the .js code that I currently use so that anyone who wants to join and help improve it can make their PRs and also for those C developers who want to help create the native Flipper app and be accessible by everyone from https://lab.flipper.net/apps -# How it is work +## How it is work Currently the code takes the api key in plain text stored in: @@ -18,7 +18,7 @@ I added a menu to show stored APs so the user just need to select to one that wa I added the source code of the new firmware for the esp32 and also the pre-compile binary for ESP-Flasher app (You can find the binary in the relases section) -# How to flash the firmware using ESP-Flasher +## How to flash the firmware using ESP-Flasher Go to "Manual flash" @@ -35,7 +35,7 @@ Flash it (you need to reboot the board for start using the firmware) ![Captura de pantalla 2024-09-28 213123](https://github.com/user-attachments/assets/031063aa-c4bf-4fbe-baa6-745573cc8411) -# TO DO +## TO DO --> ~~improve the handling of how the APs are saved, currently it saves them but rewrites the previous one~~ Fixed @@ -43,8 +43,12 @@ Flash it (you need to reboot the board for start using the firmware) --> ~~load the firmware binary for the esp32 (nothing to do here just load the binary)~~ -# Screenshots +## Screenshots ![image](https://github.com/user-attachments/assets/3878b4a2-223d-4d23-b395-2d25cf710fed) ![image](https://github.com/user-attachments/assets/777e2d55-f9fd-4c63-bb47-450b020b80e0) + +## Native app + +The native app is in development. Currently it just shows the main menu and then each option shows "Under construction". diff --git a/application.fam b/application.fam new file mode 100644 index 00000000000..528335f24ba --- /dev/null +++ b/application.fam @@ -0,0 +1,12 @@ +App( + appid="gemini_ia", + name="Gemini IA", + apptype=FlipperAppType.EXTERNAL, + entry_point="gemini_app", + requires=["gui"], + stack_size=2 * 1024, + fap_icon="gemini.png", + fap_category="Misc", + fap_file_assets="Gemini IA", + fap_description="This is an app to interact with Google Gemini IA using the esp32.", +) diff --git a/gemini.png b/gemini.png new file mode 100644 index 0000000000000000000000000000000000000000..56123caeb15de3a2a9e54d239822314d2e846502 GIT binary patch literal 145 zcmeAS@N?(olHy`uVBq!ia0vp^AT}2VGmzZ%#=aj&u?6^qxc>kDAIJl$$Gjt zhE&W+PB_3S;mB~PNjN}AT6mR&r|u+90|o{bH3m`l7N3P6g(a>LCBgY=CFO}lsSFM| mrFn@3iJ5sN`UQFEy2<$|smWiRCFOt$89ZJ6T-G@yGywntqbDB# literal 0 HcmV?d00001 diff --git a/gemini_app.c b/gemini_app.c new file mode 100644 index 00000000000..1114bbb18e9 --- /dev/null +++ b/gemini_app.c @@ -0,0 +1,53 @@ +#include "gemini_app_i.h" + +static bool gemini_app_custom_callback(void* context, uint32_t custom_event) { + furi_assert(context); + GeminiApp* app = context; + return scene_manager_handle_custom_event(app->scene_manager, custom_event); +} + +static bool gemini_app_back_event_callback(void* context) { + furi_assert(context); + GeminiApp* app = context; + return scene_manager_handle_back_event(app->scene_manager); +} + +static GeminiApp* gemini_app_alloc() { + GeminiApp* app = malloc(sizeof(GeminiApp)); + app->scene_manager = scene_manager_alloc(&gemini_scene_handlers, app); + app->view_dispatcher = view_dispatcher_alloc(); + view_dispatcher_set_event_callback_context(app->view_dispatcher, app); + view_dispatcher_set_custom_event_callback(app->view_dispatcher, gemini_app_custom_callback); + view_dispatcher_set_navigation_event_callback( + app->view_dispatcher, gemini_app_back_event_callback); + app->submenu = submenu_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, GeminiViewSubmenu, submenu_get_view(app->submenu)); + app->widget = widget_alloc(); + view_dispatcher_add_view(app->view_dispatcher, GeminiViewWidget, widget_get_view(app->widget)); + return app; +} + +static void gemini_app_free(GeminiApp* app) { + furi_assert(app); + view_dispatcher_remove_view(app->view_dispatcher, GeminiViewSubmenu); + view_dispatcher_remove_view(app->view_dispatcher, GeminiViewWidget); + scene_manager_free(app->scene_manager); + view_dispatcher_free(app->view_dispatcher); + submenu_free(app->submenu); + widget_free(app->widget); + free(app); +} + +int32_t gemini_app(void* p) { + UNUSED(p); + GeminiApp* app = gemini_app_alloc(); + + Gui* gui = furi_record_open(RECORD_GUI); + view_dispatcher_attach_to_gui(app->view_dispatcher, gui, ViewDispatcherTypeFullscreen); + scene_manager_next_scene(app->scene_manager, GeminiSceneMainMenu); + view_dispatcher_run(app->view_dispatcher); + + gemini_app_free(app); + return 0; +} diff --git a/gemini_app.h b/gemini_app.h new file mode 100644 index 00000000000..d51e1cbea4a --- /dev/null +++ b/gemini_app.h @@ -0,0 +1,6 @@ +#pragma once + +typedef enum { + GeminiViewSubmenu, + GeminiViewWidget, +} GeminiView; diff --git a/gemini_app_i.h b/gemini_app_i.h new file mode 100644 index 00000000000..8c69e4dd54a --- /dev/null +++ b/gemini_app_i.h @@ -0,0 +1,18 @@ +#pragma once + +#include "gemini_app.h" + +#include +#include +#include +#include + +#include "scenes/gemini_scene.h" + +typedef struct GeminiApp GeminiApp; +struct GeminiApp { + SceneManager* scene_manager; + ViewDispatcher* view_dispatcher; + Submenu* submenu; + Widget* widget; +}; diff --git a/scenes/gemini_scene.c b/scenes/gemini_scene.c new file mode 100644 index 00000000000..c32ff452c22 --- /dev/null +++ b/scenes/gemini_scene.c @@ -0,0 +1,30 @@ +#include "gemini_scene.h" + +// Generate scene on_enter handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter, +void (*const gemini_scene_on_enter_handlers[])(void*) = { +#include "gemini_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 gemini_scene_on_event_handlers[])(void* context, SceneManagerEvent event) = { +#include "gemini_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 gemini_scene_on_exit_handlers[])(void* context) = { +#include "gemini_scene_config.h" +}; +#undef ADD_SCENE + +// Initialize scene handlers configuration structure +const SceneManagerHandlers gemini_scene_handlers = { + .on_enter_handlers = gemini_scene_on_enter_handlers, + .on_event_handlers = gemini_scene_on_event_handlers, + .on_exit_handlers = gemini_scene_on_exit_handlers, + .scene_num = GeminiSceneNum, +}; \ No newline at end of file diff --git a/scenes/gemini_scene.h b/scenes/gemini_scene.h new file mode 100644 index 00000000000..570cd029087 --- /dev/null +++ b/scenes/gemini_scene.h @@ -0,0 +1,29 @@ +#pragma once + +#include + +// Generate scene id and total number +#define ADD_SCENE(prefix, name, id) GeminiScene##id, +typedef enum { +#include "gemini_scene_config.h" + GeminiSceneNum, +} GeminiScene; +#undef ADD_SCENE + +extern const SceneManagerHandlers gemini_scene_handlers; + +// Generate scene on_enter handlers declaration +#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*); +#include "gemini_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 "gemini_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 "gemini_scene_config.h" +#undef ADD_SCENE diff --git a/scenes/gemini_scene_config.h b/scenes/gemini_scene_config.h new file mode 100644 index 00000000000..0dbfe8df1c1 --- /dev/null +++ b/scenes/gemini_scene_config.h @@ -0,0 +1,2 @@ +ADD_SCENE(gemini, main_menu, MainMenu) +ADD_SCENE(gemini, under_construction, UnderConstruction) diff --git a/scenes/gemini_scene_main_menu.c b/scenes/gemini_scene_main_menu.c new file mode 100644 index 00000000000..04d9421556b --- /dev/null +++ b/scenes/gemini_scene_main_menu.c @@ -0,0 +1,130 @@ +#include "../gemini_app_i.h" + +typedef enum { + GeminiSceneMainMenuIndexSetName, + GeminiSceneMainMenuIndexConnectNewAP, + GeminiSceneMainMenuIndexConnectSavedAP, + GeminiSceneMainMenuIndexStartChatting, + GeminiSceneMainMenuIndexHelp, +} GeminiSceneMainMenuIndex; + +typedef enum { + GeminiSceneMainMenuEventSetName, + GeminiSceneMainMenuEventConnectNewAP, + GeminiSceneMainMenuEventConnectSavedAP, + GeminiSceneMainMenuEventStartChatting, + GeminiSceneMainMenuEventHelp, +} GeminiSceneMainMenuEvent; + +static void gemini_scene_main_menu_callback(void* context, uint32_t index) { + GeminiApp* app = context; + switch(index) { + case GeminiSceneMainMenuIndexSetName: + scene_manager_handle_custom_event( + app->scene_manager, GeminiSceneMainMenuEventSetName); + break; + case GeminiSceneMainMenuIndexConnectNewAP: + scene_manager_handle_custom_event( + app->scene_manager, GeminiSceneMainMenuEventConnectNewAP); + break; + case GeminiSceneMainMenuIndexConnectSavedAP: + scene_manager_handle_custom_event( + app->scene_manager, GeminiSceneMainMenuEventConnectSavedAP); + break; + case GeminiSceneMainMenuIndexStartChatting: + scene_manager_handle_custom_event( + app->scene_manager, GeminiSceneMainMenuEventStartChatting); + break; + case GeminiSceneMainMenuIndexHelp: + scene_manager_handle_custom_event( + app->scene_manager, GeminiSceneMainMenuEventHelp); + break; + } +} + +void gemini_scene_main_menu_on_enter(void* context) { + GeminiApp* app = context; + submenu_reset(app->submenu); + submenu_set_header(app->submenu, "Gemini IA"); + submenu_add_item( + app->submenu, + "Set your name", + GeminiSceneMainMenuIndexSetName, + gemini_scene_main_menu_callback, + app); + submenu_add_item( + app->submenu, + "Connect to new AP", + GeminiSceneMainMenuIndexConnectNewAP, + gemini_scene_main_menu_callback, + app); + submenu_add_item( + app->submenu, + "Connect to saved AP", + GeminiSceneMainMenuIndexConnectSavedAP, + gemini_scene_main_menu_callback, + app); + submenu_add_item( + app->submenu, + "Start Chatting", + GeminiSceneMainMenuIndexStartChatting, + gemini_scene_main_menu_callback, + app); + submenu_add_item( + app->submenu, + "Help", + GeminiSceneMainMenuIndexHelp, + gemini_scene_main_menu_callback, + app); + + uint32_t index = scene_manager_get_scene_state(app->scene_manager, GeminiSceneMainMenu); + submenu_set_selected_item(app->submenu, index); + + view_dispatcher_switch_to_view(app->view_dispatcher, GeminiViewSubmenu); +} + +bool gemini_scene_main_menu_on_event(void* context, SceneManagerEvent event) { + GeminiApp* app = context; + bool consumed = false; + switch(event.type) { + case SceneManagerEventTypeCustom: + switch(event.event) { + case GeminiSceneMainMenuEventSetName: + // TODO: Replace with correct scene + scene_manager_next_scene(app->scene_manager, GeminiSceneUnderConstruction); + consumed = true; + break; + case GeminiSceneMainMenuEventConnectNewAP: + // TODO: Replace with correct scene + scene_manager_next_scene(app->scene_manager, GeminiSceneUnderConstruction); + consumed = true; + break; + case GeminiSceneMainMenuEventConnectSavedAP: + // TODO: Replace with correct scene + scene_manager_next_scene(app->scene_manager, GeminiSceneUnderConstruction); + consumed = true; + break; + case GeminiSceneMainMenuEventStartChatting: + // TODO: Replace with correct scene + scene_manager_next_scene(app->scene_manager, GeminiSceneUnderConstruction); + consumed = true; + break; + case GeminiSceneMainMenuEventHelp: + // TODO: Replace with correct scene + scene_manager_next_scene(app->scene_manager, GeminiSceneUnderConstruction); + consumed = true; + break; + } + break; + default: + break; + } + + return consumed; +} + +void gemini_scene_main_menu_on_exit(void* context) { + GeminiApp* app = context; + scene_manager_set_scene_state(app->scene_manager, GeminiSceneMainMenu, submenu_get_selected_item(app->submenu)); + submenu_reset(app->submenu); +} \ No newline at end of file diff --git a/scenes/gemini_scene_under_construction.c b/scenes/gemini_scene_under_construction.c new file mode 100644 index 00000000000..11f86e7950c --- /dev/null +++ b/scenes/gemini_scene_under_construction.c @@ -0,0 +1,19 @@ +#include "../gemini_app_i.h" + +void gemini_scene_under_construction_on_enter(void* context) { + GeminiApp* app = context; + widget_reset(app->widget); + widget_add_string_element( + app->widget, 0, 25, AlignLeft, AlignTop, FontPrimary, "UNDER CONSTRUCTION"); + view_dispatcher_switch_to_view(app->view_dispatcher, GeminiViewWidget); +} + +bool gemini_scene_under_construction_on_event(void* context, SceneManagerEvent event) { + UNUSED(context); + UNUSED(event); + return false; // event not handled. +} + +void gemini_scene_under_construction_on_exit(void* context) { + UNUSED(context); +} \ No newline at end of file