Explorar o código

half way done with github installation support

jblanked hai 1 ano
pai
achega
0c05e2669a

+ 163 - 24
callback/flip_store_callback.c

@@ -1,4 +1,5 @@
 #include <callback/flip_store_callback.h>
+#include <github/flip_store_github.h>
 
 // Below added by Derek Jamison
 // FURI_LOG_DEV will log only during app development. Be sure that Settings/System/Log Device is "LPUART"; so we dont use serial port.
@@ -254,7 +255,8 @@ static bool flip_store_input_callback(InputEvent *event, void *context)
 
     return false;
 }
-
+static void free_text_input_view(FlipStoreApp *app);
+static bool alloc_text_input_view(void *context, char *title);
 static void flip_store_text_updated_ssid(void *context)
 {
     FlipStoreApp *app = (FlipStoreApp *)context;
@@ -367,6 +369,103 @@ static void flip_store_text_updated_pass(void *context)
     // switch to the settings view
     view_dispatcher_switch_to_view(app->view_dispatcher, FlipStoreViewSettings);
 }
+static void flip_store_text_updated_repo(void *context);
+static void flip_store_text_updated_author(void *context)
+{
+    FlipStoreApp *app = (FlipStoreApp *)context;
+    if (!app)
+    {
+        FURI_LOG_E(TAG, "FlipStoreApp is NULL");
+        return;
+    }
+
+    // store the entered text
+    strncpy(app->uart_text_input_buffer, app->uart_text_input_temp_buffer, app->uart_text_input_buffer_size);
+
+    // Ensure null-termination
+    app->uart_text_input_buffer[app->uart_text_input_buffer_size - 1] = '\0';
+
+    // save the setting
+    save_char("Github-Author", app->uart_text_input_buffer);
+
+    view_dispatcher_switch_to_view(app->view_dispatcher, FlipStoreViewSubmenu);
+    uart_text_input_reset(app->uart_text_input);
+    uart_text_input_set_header_text(app->uart_text_input, "Repository");
+    app->uart_text_input_buffer_size = 64;
+    free(app->uart_text_input_buffer);
+    free(app->uart_text_input_temp_buffer);
+    easy_flipper_set_buffer(&app->uart_text_input_buffer, app->uart_text_input_buffer_size);
+    easy_flipper_set_buffer(&app->uart_text_input_temp_buffer, app->uart_text_input_buffer_size);
+    uart_text_input_set_result_callback(app->uart_text_input, flip_store_text_updated_repo, app, app->uart_text_input_temp_buffer, app->uart_text_input_buffer_size, false);
+    view_dispatcher_switch_to_view(app->view_dispatcher, FlipStoreViewTextInput);
+}
+
+static bool flip_store_fetch_github(DataLoaderModel *model)
+{
+    if (!model || !model->fhttp)
+    {
+        FURI_LOG_E(TAG, "Model/FlipperHTTP is NULL");
+        return false;
+    }
+
+    if (model->request_index == 0)
+    {
+        char author[64];
+        char repo[64];
+        if (!load_char("Github-Author", author, sizeof(author)) || !load_char("Github-Repo", repo, sizeof(repo)))
+        {
+            FURI_LOG_E(TAG, "Failed to load Github author or repo");
+            return false;
+        }
+
+        return flip_store_get_github_contents(model->fhttp, author, repo);
+    }
+    return false; // return false for now
+}
+
+static char *flip_store_parse_github(DataLoaderModel *model)
+{
+    if (model->request_index == 0)
+    {
+        char author[64];
+        char repo[64];
+        if (!load_char("Github-Author", author, sizeof(author)) || !load_char("Github-Repo", repo, sizeof(repo)))
+        {
+            FURI_LOG_E(TAG, "Failed to load Github author or repo");
+            return "Failed to load Github author or repo";
+        }
+        if (!flip_store_parse_github_contents(model->fhttp->file_path, author, repo))
+        {
+            return "Failed to parse Github contents";
+        }
+        return "Repository contents fetched...";
+    }
+    return "Failed to download repository.";
+}
+static void flip_store_github_switch_to_view(FlipStoreApp *app)
+{
+    flip_store_generic_switch_to_view(app, "Downloading Repository..", flip_store_fetch_github, flip_store_parse_github, 1, callback_to_submenu_options, FlipStoreViewLoader);
+}
+static void flip_store_text_updated_repo(void *context)
+{
+    FlipStoreApp *app = (FlipStoreApp *)context;
+    if (!app)
+    {
+        FURI_LOG_E(TAG, "FlipStoreApp is NULL");
+        return;
+    }
+
+    // store the entered text
+    strncpy(app->uart_text_input_buffer, app->uart_text_input_temp_buffer, app->uart_text_input_buffer_size);
+
+    // Ensure null-termination
+    app->uart_text_input_buffer[app->uart_text_input_buffer_size - 1] = '\0';
+
+    // save the setting
+    save_char("Github-Repo", app->uart_text_input_buffer);
+    view_dispatcher_switch_to_view(app->view_dispatcher, FlipStoreViewSubmenuOptions);
+    flip_store_github_switch_to_view(app);
+}
 static void free_category_submenu(FlipStoreApp *app)
 {
     if (!app)
@@ -515,34 +614,58 @@ static bool alloc_text_input_view(void *context, char *title)
     }
     if (!app->uart_text_input)
     {
-        if (!easy_flipper_set_uart_text_input(
-                &app->uart_text_input,
-                FlipStoreViewTextInput,
-                title,
-                app->uart_text_input_temp_buffer,
-                app->uart_text_input_buffer_size,
-                strcmp(title, "SSID") == 0 ? flip_store_text_updated_ssid : flip_store_text_updated_pass,
-                callback_to_wifi_settings,
-                &app->view_dispatcher,
-                app))
+        if (strcmp(title, "SSID") != 0 && strcmp(title, "Password") != 0)
         {
-            return false;
-        }
-        if (!app->uart_text_input)
-        {
-            return false;
+            // Github repository download
+            if (!easy_flipper_set_uart_text_input(
+                    &app->uart_text_input,
+                    FlipStoreViewTextInput,
+                    title,
+                    app->uart_text_input_temp_buffer,
+                    app->uart_text_input_buffer_size,
+                    strcmp(title, "Author") == 0 ? flip_store_text_updated_author : flip_store_text_updated_repo,
+                    callback_to_submenu_options,
+                    &app->view_dispatcher,
+                    app))
+            {
+                return false;
+            }
+            if (!app->uart_text_input)
+            {
+                return false;
+            }
         }
-        char ssid[64];
-        char pass[64];
-        if (load_settings(ssid, sizeof(ssid), pass, sizeof(pass)))
+        else
         {
-            if (strcmp(title, "SSID") == 0)
+            if (!easy_flipper_set_uart_text_input(
+                    &app->uart_text_input,
+                    FlipStoreViewTextInput,
+                    title,
+                    app->uart_text_input_temp_buffer,
+                    app->uart_text_input_buffer_size,
+                    strcmp(title, "SSID") == 0 ? flip_store_text_updated_ssid : flip_store_text_updated_pass,
+                    callback_to_wifi_settings,
+                    &app->view_dispatcher,
+                    app))
             {
-                strncpy(app->uart_text_input_temp_buffer, ssid, app->uart_text_input_buffer_size);
+                return false;
             }
-            else
+            if (!app->uart_text_input)
+            {
+                return false;
+            }
+            char ssid[64];
+            char pass[64];
+            if (load_settings(ssid, sizeof(ssid), pass, sizeof(pass)))
             {
-                strncpy(app->uart_text_input_temp_buffer, pass, app->uart_text_input_buffer_size);
+                if (strcmp(title, "SSID") == 0)
+                {
+                    strncpy(app->uart_text_input_temp_buffer, ssid, app->uart_text_input_buffer_size);
+                }
+                else
+                {
+                    strncpy(app->uart_text_input_temp_buffer, pass, app->uart_text_input_buffer_size);
+                }
             }
         }
     }
@@ -739,6 +862,7 @@ uint32_t callback_to_submenu_options(void *context)
         return FlipStoreViewSubmenuOptions;
     }
     firmware_free();
+    vgm_firmware_free();
     flip_catalog_free();
     free_category_submenu(app);
     return FlipStoreViewSubmenuOptions;
@@ -818,12 +942,18 @@ static void fetch_appropiate_app_list(FlipStoreApp *app, int iteration)
     }
     bool fetch_app_list()
     {
+        // ensure /apps_data/flip_store/data exists
+        Storage *storage = furi_record_open(RECORD_STORAGE);
+        char dir[256];
+        snprintf(dir, sizeof(dir), STORAGE_EXT_PATH_PREFIX "/apps_data/flip_store/data");
+        storage_common_mkdir(storage, dir);
+        furi_record_close(RECORD_STORAGE);
         fhttp->state = IDLE;
         flip_catalog_free();
         snprintf(
             fhttp->file_path,
             sizeof(fhttp->file_path),
-            STORAGE_EXT_PATH_PREFIX "/apps_data/flip_store/%s.json", categories[flip_store_category_index]);
+            STORAGE_EXT_PATH_PREFIX "/apps_data/flip_store/data/%s.json", categories[flip_store_category_index]);
         fhttp->save_received_data = true;
         fhttp->is_bytes_request = false;
         char url[256];
@@ -907,6 +1037,15 @@ void callback_submenu_choices(void *context, uint32_t index)
         }
         view_dispatcher_switch_to_view(app->view_dispatcher, FlipStoreViewVGMFirmwares);
         break;
+    case FlipStoreSubmenuIndexGitHub: // github
+        free_all_views(app, true);
+        if (!alloc_text_input_view(app, "Author"))
+        {
+            FURI_LOG_E(TAG, "Failed to allocate text input view");
+            return;
+        }
+        view_dispatcher_switch_to_view(app->view_dispatcher, FlipStoreViewTextInput);
+        break;
     case FlipStoreSubmenuIndexAppListBluetooth:
         free_all_views(app, true);
         flip_store_category_index = 0;

+ 1 - 8
firmwares/flip_store_firmwares.c

@@ -102,12 +102,5 @@ bool flip_store_get_firmware_file(FlipperHTTP *fhttp, char *link, char *name, ch
     furi_record_close(RECORD_STORAGE);
     fhttp->save_received_data = false;
     fhttp->is_bytes_request = true;
-    bool sent_request = flipper_http_get_request_bytes(fhttp, link, "{\"Content-Type\":\"application/octet-stream\"}");
-    if (sent_request)
-    {
-        fhttp->state = RECEIVING;
-        return true;
-    }
-    fhttp->state = ISSUE;
-    return false;
+    return flipper_http_get_request_bytes(fhttp, link, "{\"Content-Type\":\"application/octet-stream\"}");
 }

+ 40 - 3
flip_storage/flip_store_storage.c

@@ -326,7 +326,7 @@ bool save_char(
     }
     // Create the directory for saving settings
     char directory_path[256];
-    snprintf(directory_path, sizeof(directory_path), STORAGE_EXT_PATH_PREFIX "/apps_data/flip_store");
+    snprintf(directory_path, sizeof(directory_path), STORAGE_EXT_PATH_PREFIX "/apps_data/flip_store/data");
 
     // Create the directory
     Storage *storage = furi_record_open(RECORD_STORAGE);
@@ -335,7 +335,7 @@ bool save_char(
     // Open the settings file
     File *file = storage_file_alloc(storage);
     char file_path[256];
-    snprintf(file_path, sizeof(file_path), STORAGE_EXT_PATH_PREFIX "/apps_data/flip_store/%s.txt", path_name);
+    snprintf(file_path, sizeof(file_path), STORAGE_EXT_PATH_PREFIX "/apps_data/flip_store/data/%s.txt", path_name);
 
     // Open the file in write mode
     if (!storage_file_open(file, file_path, FSAM_WRITE, FSOM_CREATE_ALWAYS))
@@ -377,7 +377,7 @@ bool load_char(
     File *file = storage_file_alloc(storage);
 
     char file_path[256];
-    snprintf(file_path, sizeof(file_path), STORAGE_EXT_PATH_PREFIX "/apps_data/flip_store/%s.txt", path_name);
+    snprintf(file_path, sizeof(file_path), STORAGE_EXT_PATH_PREFIX "/apps_data/flip_store/data/%s.txt", path_name);
 
     // Open the file for reading
     if (!storage_file_open(file, file_path, FSAM_READ, FSOM_OPEN_EXISTING))
@@ -405,5 +405,42 @@ bool load_char(
     storage_file_free(file);
     furi_record_close(RECORD_STORAGE);
 
+    return true;
+}
+
+bool save_char_with_path(const char *full_path, const char *value)
+{
+    if (!value)
+    {
+        return false;
+    }
+
+    Storage *storage = furi_record_open(RECORD_STORAGE);
+    File *file = storage_file_alloc(storage);
+
+    // Open the file in write mode
+    if (!storage_file_open(file, full_path, FSAM_WRITE, FSOM_CREATE_ALWAYS))
+    {
+        FURI_LOG_E(HTTP_TAG, "Failed to open file for writing: %s", full_path);
+        storage_file_free(file);
+        furi_record_close(RECORD_STORAGE);
+        return false;
+    }
+
+    // Write the data to the file
+    size_t data_size = strlen(value) + 1; // Include null terminator
+    if (storage_file_write(file, value, data_size) != data_size)
+    {
+        FURI_LOG_E(HTTP_TAG, "Failed to append data to file");
+        storage_file_close(file);
+        storage_file_free(file);
+        furi_record_close(RECORD_STORAGE);
+        return false;
+    }
+
+    storage_file_close(file);
+    storage_file_free(file);
+    furi_record_close(RECORD_STORAGE);
+
     return true;
 }

+ 2 - 0
flip_storage/flip_store_storage.h

@@ -35,4 +35,6 @@ bool load_char(
     char *value,
     size_t value_size);
 
+bool save_char_with_path(const char *full_path, const char *value);
+
 #endif

+ 2 - 0
flip_store.h

@@ -24,6 +24,8 @@
 #define VGM_FIRMWARE_COUNT 1
 #define VGM_FIRMWARE_LINKS 1
 
+#define MAX_GITHUB_FILES 30
+
 // Define the submenu items for our FlipStore application
 typedef enum
 {

+ 1 - 1
flipper_http/flipper_http.c

@@ -1321,7 +1321,7 @@ void flipper_http_rx_callback(const char *line, void *context)
     }
 
     // Uncomment below line to log the data received over UART
-    // FURI_LOG_I(HTTP_TAG, "Received UART line: %s", line);
+    FURI_LOG_I(HTTP_TAG, "Received UART line: %s", line);
 
     // Check if we've started receiving data from a GET request
     if (fhttp->started_receiving_get)

+ 174 - 0
github/flip_store_github.c

@@ -0,0 +1,174 @@
+#include <github/flip_store_github.h>
+#include <flip_storage/flip_store_storage.h>
+
+// Helper to download a file from Github and save it to the storage
+bool flip_store_download_github_file(
+    FlipperHTTP *fhttp,
+    const char *filename,
+    const char *author,
+    const char *repo,
+    const char *link)
+{
+    if (!fhttp)
+    {
+        FURI_LOG_E(TAG, "FlipperHTTP is NULL");
+        return false;
+    }
+
+    // Create the file directory
+    char dir[256];
+    snprintf(dir, sizeof(dir), STORAGE_EXT_PATH_PREFIX "/apps_data/flip_store/%s/%s/%s", author, repo, filename);
+    snprintf(fhttp->file_path, sizeof(fhttp->file_path), "%s", dir);
+    Storage *storage = furi_record_open(RECORD_STORAGE);
+    storage_common_mkdir(storage, dir);
+    furi_record_close(RECORD_STORAGE);
+
+    fhttp->state = IDLE;
+    fhttp->save_received_data = false;
+    fhttp->is_bytes_request = true;
+
+    return flipper_http_get_request_bytes(fhttp, link, "{\"Content-Type\":\"application/octet-stream\"}");
+}
+
+bool flip_store_get_github_contents(FlipperHTTP *fhttp, const char *author, const char *repo)
+{
+    // Create Initial directory
+    Storage *storage = furi_record_open(RECORD_STORAGE);
+
+    char dir[256];
+
+    // create a data directory: /ext/apps_data/flip_store/data
+    snprintf(dir, sizeof(dir), STORAGE_EXT_PATH_PREFIX "/apps_data/flip_store/data");
+    storage_common_mkdir(storage, dir);
+
+    // create a data directory for the author: /ext/apps_data/flip_store/data/author
+    snprintf(dir, sizeof(dir), STORAGE_EXT_PATH_PREFIX "/apps_data/flip_store/data/%s", author);
+    storage_common_mkdir(storage, dir);
+
+    // example path: /ext/apps_data/flip_store/data/author/info.json
+    snprintf(fhttp->file_path, sizeof(fhttp->file_path), STORAGE_EXT_PATH_PREFIX "/apps_data/flip_store/data/%s/info.json", author);
+
+    // create a data directory for the repo: /ext/apps_data/flip_store/data/author/repo
+    snprintf(dir, sizeof(dir), STORAGE_EXT_PATH_PREFIX "/apps_data/flip_store/data/%s/%s", author, repo);
+    storage_common_mkdir(storage, dir);
+
+    furi_record_close(RECORD_STORAGE);
+
+    // get the contents of the repo
+    char link[256];
+    snprintf(link, sizeof(link), "https://api.github.com/repos/%s/%s/contents", author, repo);
+    fhttp->save_received_data = true;
+    return flipper_http_get_request_with_headers(fhttp, link, "{\"Content-Type\":\"application/json\"}");
+}
+
+bool flip_store_parse_github_contents(char *file_path, const char *author, const char *repo)
+{
+    // Parse list of files and prepare to download
+    FuriString *git_data = flipper_http_load_from_file(file_path);
+    if (git_data == NULL)
+    {
+        FURI_LOG_E(TAG, "Failed to load received data from file.");
+        return false;
+    }
+    FuriString *git_data_str = furi_string_alloc();
+    if (!git_data_str)
+    {
+        FURI_LOG_E("Game", "Failed to allocate json_data string");
+        return false;
+    }
+    furi_string_cat_str(git_data_str, "{\"json_data\":");
+    if (memmgr_get_free_heap() < furi_string_size(git_data) + furi_string_size(git_data_str) + 2)
+    {
+        FURI_LOG_E(TAG, "Not enough memory to allocate git_data_str.");
+        furi_string_free(git_data);
+        furi_string_free(git_data_str);
+        return false;
+    }
+    furi_string_cat(git_data_str, git_data);
+    furi_string_free(git_data);
+    furi_string_cat_str(git_data_str, "}");
+    //
+    int file_count = 0;
+    for (int i = 0; i < MAX_GITHUB_FILES; i++)
+    {
+        FuriString *json_data_array = get_json_array_value_furi("json_data", i, git_data_str);
+        if (!json_data_array)
+        {
+            break;
+        }
+        FuriString *type = get_json_value_furi("type", json_data_array);
+        if (!type)
+        {
+            FURI_LOG_E(TAG, "Failed to get type.");
+            furi_string_free(json_data_array);
+            break;
+        }
+        // skip directories for now
+        if (strcmp(furi_string_get_cstr(type), "file") != 0)
+        {
+            furi_string_free(type);
+            furi_string_free(json_data_array);
+            continue;
+        }
+        furi_string_free(type);
+        FuriString *download_url = get_json_value_furi("download_url", json_data_array);
+        if (!download_url)
+        {
+            FURI_LOG_E(TAG, "Failed to get download_url.");
+
+            furi_string_free(json_data_array);
+            break;
+        }
+        FuriString *name = get_json_value_furi("name", json_data_array);
+        if (!name)
+        {
+            FURI_LOG_E(TAG, "Failed to get name.");
+
+            furi_string_free(json_data_array);
+            furi_string_free(download_url);
+            break;
+        }
+        furi_string_free(json_data_array);
+
+        // create json to save
+        FuriString *json = furi_string_alloc();
+        if (!json)
+        {
+            FURI_LOG_E(TAG, "Failed to allocate json.");
+
+            furi_string_free(json_data_array);
+            furi_string_free(download_url);
+            furi_string_free(name);
+            break;
+        }
+
+        furi_string_cat_str(json, "{\"name\":\"");
+        furi_string_cat(json, name);
+
+        furi_string_cat_str(json, "\",\"link\":\"");
+        furi_string_cat(json, download_url);
+        furi_string_free(download_url);
+
+        furi_string_cat_str(json, "\"}");
+
+        // save the json to the data folder: /ext/apps_data/flip_store/data/author/repo
+        char dir[256];
+        snprintf(dir, sizeof(dir), STORAGE_EXT_PATH_PREFIX "/apps_data/flip_store/data/%s/%s/%s.json", author, repo, furi_string_get_cstr(name));
+        if (!save_char_with_path(dir, furi_string_get_cstr(json)))
+        {
+            FURI_LOG_E(TAG, "Failed to save json.");
+        }
+
+        // free the json
+        furi_string_free(name);
+        furi_string_free(json);
+        file_count++;
+    }
+    furi_string_free(git_data_str);
+    // save file count
+    char file_count_str[16];
+    snprintf(file_count_str, sizeof(file_count_str), "%d", file_count);
+    char file_count_dir[256]; // /ext/apps_data/flip_store/data/author/file_count.txt
+    snprintf(file_count_dir, sizeof(file_count_dir), STORAGE_EXT_PATH_PREFIX "/apps_data/flip_store/data/%s/file_count.txt", author);
+    return save_char_with_path(file_count_dir, file_count_str);
+}

+ 13 - 0
github/flip_store_github.h

@@ -0,0 +1,13 @@
+#pragma once
+#include <flip_store.h>
+
+// Helper to download a file from Github and save it to the storage
+bool flip_store_download_github_file(
+    FlipperHTTP *fhttp,
+    const char *filename,
+    const char *author,
+    const char *repo,
+    const char *link);
+
+bool flip_store_get_github_contents(FlipperHTTP *fhttp, const char *author, const char *repo);
+bool flip_store_parse_github_contents(char *file_path, const char *author, const char *repo);