Просмотр исходного кода

FlipStore - v0.4

- Added an option to delete apps
- Edit by Willy-JL
jblanked 1 год назад
Родитель
Сommit
54b7323d5e

+ 2 - 0
README.md

@@ -51,3 +51,5 @@ This is a big task, and I welcome all contributors, especially developers intere
    - This issue has been addressed, but it may still occur. If it does, restart the app.
 2. The app file is corrupted.
    - This is likely due to an error parsing the data. Restart the app and wait until the green LED light turns off after downloading the app before exiting the view. If this happens more than three times, the current version of FlipStore may not be able to download that app successfully.
+3. The app is frozen on the "Installing" or "Receiving data" screen. 
+   - If it there LED is not on and it's been more than 5 seconds, restart your Flipper Zero with the devboard plugged in.

+ 1 - 1
alloc/flip_store_alloc.c

@@ -105,7 +105,7 @@ FlipStoreApp *flip_store_app_alloc()
     app->variable_item_pass = variable_item_list_add(app->variable_item_list, "Password", 0, NULL, NULL);
 
     // Submenu
-    if (!easy_flipper_set_submenu(&app->submenu, FlipStoreViewSubmenu, "FlipStore v0.3", callback_exit_app, &app->view_dispatcher))
+    if (!easy_flipper_set_submenu(&app->submenu, FlipStoreViewSubmenu, "FlipStore v0.4", callback_exit_app, &app->view_dispatcher))
     {
         return NULL;
     }

+ 4 - 0
assets/CHANGELOG.md

@@ -1,3 +1,7 @@
+## v0.4
+- Added an option to delete apps
+- Edit by Willy-JL
+
 ## v0.3
 - Edits by Willy-JL
 - Improved memory allocation

+ 2 - 0
assets/README.md

@@ -51,3 +51,5 @@ This is a big task, and I welcome all contributors, especially developers intere
    - This issue has been addressed, but it may still occur. If it does, restart the app.
 2. The app file is corrupted.
    - This is likely due to an error parsing the data. Restart the app and wait until the green LED light turns off after downloading the app before exiting the view. If this happens more than three times, the current version of FlipStore may not be able to download that app successfully.
+3. The app is frozen on the "Installing" or "Receiving data" screen. 
+   - If it there LED is not on and it's been more than 5 seconds, restart your Flipper Zero with the devboard plugged in.

+ 52 - 24
callback/flip_store_callback.c

@@ -1,5 +1,7 @@
 #include <callback/flip_store_callback.h>
 
+bool flip_store_app_does_exist = false;
+
 // Callback for drawing the main screen
 void flip_store_view_draw_callback_main(Canvas *canvas, void *model)
 {
@@ -60,12 +62,19 @@ void flip_store_view_draw_callback_app_list(Canvas *canvas, void *model)
     UNUSED(model);
     canvas_clear(canvas);
     canvas_set_font(canvas, FontPrimary);
-    // Adjusted to access flip_catalog as an array of structures
     canvas_draw_str(canvas, 0, 10, flip_catalog[app_selected_index].app_name);
-    // canvas_draw_icon(canvas, 0, 53, &I_ButtonLeft_4x7); (future implementation)
-    //  canvas_draw_str_aligned(canvas, 7, 54, AlignLeft, AlignTop, "Delete");  (future implementation)
-    canvas_draw_icon(canvas, 0, 53, &I_ButtonBACK_10x8);
-    canvas_draw_str_aligned(canvas, 12, 54, AlignLeft, AlignTop, "Back");
+    if (flip_store_app_does_exist)
+    {
+        canvas_draw_icon(canvas, 0, 53, &I_ButtonLeft_4x7);
+        canvas_draw_str_aligned(canvas, 7, 54, AlignLeft, AlignTop, "Delete");
+        canvas_draw_icon(canvas, 45, 53, &I_ButtonBACK_10x8);
+        canvas_draw_str_aligned(canvas, 57, 54, AlignLeft, AlignTop, "Back");
+    }
+    else
+    {
+        canvas_draw_icon(canvas, 0, 53, &I_ButtonBACK_10x8);
+        canvas_draw_str_aligned(canvas, 12, 54, AlignLeft, AlignTop, "Back");
+    }
     canvas_draw_icon(canvas, 90, 53, &I_ButtonRight_4x7);
     canvas_draw_str_aligned(canvas, 97, 54, AlignLeft, AlignTop, "Install");
 }
@@ -80,13 +89,12 @@ bool flip_store_input_callback(InputEvent *event, void *context)
     }
     if (event->type == InputTypeShort)
     {
-        // Future implementation
-        // if (event->key == InputKeyLeft)
-        //{
-        // Left button clicked, delete the app with DialogEx confirmation
-        // view_dispatcher_switch_to_view(app->view_dispatcher, FlipStoreViewAppDelete);
-        //    return true;
-        //}
+        if (event->key == InputKeyLeft && flip_store_app_does_exist)
+        {
+            // Left button clicked, delete the app
+            view_dispatcher_switch_to_view(app->view_dispatcher, FlipStoreViewAppDelete);
+            return true;
+        }
         if (event->key == InputKeyRight)
         {
             // Right button clicked, download the app
@@ -205,6 +213,7 @@ uint32_t callback_to_app_list(void *context)
     flip_store_success = false;
     flip_store_saved_data = false;
     flip_store_saved_success = false;
+    flip_store_app_does_exist = false;
     return FlipStoreViewAppList;
 }
 
@@ -241,13 +250,24 @@ void dialog_callback(DialogExResult result, void *context)
     else if (result == DialogExResultRight)
     {
         // delete the app then return to the app list
-
-        // pop up a message
-        popup_set_header(app->popup, "Success", 0, 0, AlignLeft, AlignTop);
-        popup_set_text(app->popup, "App deleted successfully.", 0, 60, AlignLeft, AlignTop);
-        view_dispatcher_switch_to_view(app->view_dispatcher, FlipStoreViewPopup);
-        furi_delay_ms(2000); // delay for 2 seconds
-        view_dispatcher_switch_to_view(app->view_dispatcher, FlipStoreViewAppList);
+        if (!delete_app(flip_catalog[app_selected_index].app_id, categories[flip_store_category_index]))
+        {
+            // pop up a message
+            popup_set_header(app->popup, "[ERROR]", 0, 0, AlignLeft, AlignTop);
+            popup_set_text(app->popup, "Issue deleting app.", 0, 50, AlignLeft, AlignTop);
+            view_dispatcher_switch_to_view(app->view_dispatcher, FlipStoreViewPopup);
+            furi_delay_ms(2000); // delay for 2 seconds
+            view_dispatcher_switch_to_view(app->view_dispatcher, FlipStoreViewAppList);
+        }
+        else
+        {
+            // pop up a message
+            popup_set_header(app->popup, "[SUCCESS]", 0, 0, AlignLeft, AlignTop);
+            popup_set_text(app->popup, "App deleted successfully.", 0, 50, AlignLeft, AlignTop);
+            view_dispatcher_switch_to_view(app->view_dispatcher, FlipStoreViewPopup);
+            furi_delay_ms(2000); // delay for 2 seconds
+            view_dispatcher_switch_to_view(app->view_dispatcher, FlipStoreViewAppList);
+        }
     }
 }
 
@@ -262,11 +282,6 @@ void popup_callback(void *context)
     view_dispatcher_switch_to_view(app->view_dispatcher, FlipStoreViewSubmenu);
 }
 
-/**
- * @brief Navigation callback for exiting the application
- * @param context The context - unused
- * @return next view id (VIEW_NONE to exit the app)
- */
 uint32_t callback_exit_app(void *context)
 {
     // Exit the application
@@ -300,50 +315,62 @@ void callback_submenu_choices(void *context, uint32_t index)
         break;
     case FlipStoreSubmenuIndexAppList:
         flip_store_category_index = 0;
+        flip_store_app_does_exist = false;
         view_dispatcher_switch_to_view(app->view_dispatcher, FlipStoreViewAppList);
         break;
     case FlipStoreSubmenuIndexAppListBluetooth:
         flip_store_category_index = 0;
+        flip_store_app_does_exist = false;
         view_dispatcher_switch_to_view(app->view_dispatcher, flip_store_handle_app_list(app, FlipStoreViewAppListBluetooth, "Bluetooth", &app->submenu_app_list_bluetooth));
         break;
     case FlipStoreSubmenuIndexAppListGames:
         flip_store_category_index = 1;
+        flip_store_app_does_exist = false;
         view_dispatcher_switch_to_view(app->view_dispatcher, flip_store_handle_app_list(app, FlipStoreViewAppListGames, "Games", &app->submenu_app_list_games));
         break;
     case FlipStoreSubmenuIndexAppListGPIO:
         flip_store_category_index = 2;
+        flip_store_app_does_exist = false;
         view_dispatcher_switch_to_view(app->view_dispatcher, flip_store_handle_app_list(app, FlipStoreViewAppListGPIO, "GPIO", &app->submenu_app_list_gpio));
         break;
     case FlipStoreSubmenuIndexAppListInfrared:
         flip_store_category_index = 3;
+        flip_store_app_does_exist = false;
         view_dispatcher_switch_to_view(app->view_dispatcher, flip_store_handle_app_list(app, FlipStoreViewAppListInfrared, "Infrared", &app->submenu_app_list_infrared));
         break;
     case FlipStoreSubmenuIndexAppListiButton:
         flip_store_category_index = 4;
+        flip_store_app_does_exist = false;
         view_dispatcher_switch_to_view(app->view_dispatcher, flip_store_handle_app_list(app, FlipStoreViewAppListiButton, "iButton", &app->submenu_app_list_ibutton));
         break;
     case FlipStoreSubmenuIndexAppListMedia:
         flip_store_category_index = 5;
+        flip_store_app_does_exist = false;
         view_dispatcher_switch_to_view(app->view_dispatcher, flip_store_handle_app_list(app, FlipStoreViewAppListMedia, "Media", &app->submenu_app_list_media));
         break;
     case FlipStoreSubmenuIndexAppListNFC:
         flip_store_category_index = 6;
+        flip_store_app_does_exist = false;
         view_dispatcher_switch_to_view(app->view_dispatcher, flip_store_handle_app_list(app, FlipStoreViewAppListNFC, "NFC", &app->submenu_app_list_nfc));
         break;
     case FlipStoreSubmenuIndexAppListRFID:
         flip_store_category_index = 7;
+        flip_store_app_does_exist = false;
         view_dispatcher_switch_to_view(app->view_dispatcher, flip_store_handle_app_list(app, FlipStoreViewAppListRFID, "RFID", &app->submenu_app_list_rfid));
         break;
     case FlipStoreSubmenuIndexAppListSubGHz:
         flip_store_category_index = 8;
+        flip_store_app_does_exist = false;
         view_dispatcher_switch_to_view(app->view_dispatcher, flip_store_handle_app_list(app, FlipStoreViewAppListSubGHz, "Sub-GHz", &app->submenu_app_list_subghz));
         break;
     case FlipStoreSubmenuIndexAppListTools:
         flip_store_category_index = 9;
+        flip_store_app_does_exist = false;
         view_dispatcher_switch_to_view(app->view_dispatcher, flip_store_handle_app_list(app, FlipStoreViewAppListTools, "Tools", &app->submenu_app_list_tools));
         break;
     case FlipStoreSubmenuIndexAppListUSB:
         flip_store_category_index = 10;
+        flip_store_app_does_exist = false;
         view_dispatcher_switch_to_view(app->view_dispatcher, flip_store_handle_app_list(app, FlipStoreViewAppListUSB, "USB", &app->submenu_app_list_usb));
         break;
     default:
@@ -363,6 +390,7 @@ void callback_submenu_choices(void *context, uint32_t index)
                 if (app_name != NULL && strlen(app_name) > 0)
                 {
                     app_selected_index = app_index;
+                    flip_store_app_does_exist = app_exists(flip_catalog[app_selected_index].app_id, categories[flip_store_category_index]);
                     view_dispatcher_switch_to_view(app->view_dispatcher, FlipStoreViewAppInfo);
                 }
                 else

+ 3 - 5
callback/flip_store_callback.h

@@ -9,6 +9,8 @@
 #include <apps/flip_store_apps.h>
 #include <flip_storage/flip_store_storage.h>
 
+extern bool flip_store_app_does_exist;
+
 // Callback for drawing the main screen
 void flip_store_view_draw_callback_main(Canvas *canvas, void *model);
 
@@ -29,11 +31,7 @@ void settings_item_selected(void *context, uint32_t index);
 void dialog_callback(DialogExResult result, void *context);
 
 void popup_callback(void *context);
-/**
- * @brief Navigation callback for exiting the application
- * @param context The context - unused
- * @return next view id (VIEW_NONE to exit the app)
- */
+
 uint32_t callback_exit_app(void *context);
 void callback_submenu_choices(void *context, uint32_t index);
 

+ 13 - 0
flip_storage/flip_store_storage.c

@@ -113,6 +113,19 @@ bool delete_app(const char *app_id, const char *app_category)
     return true;
 }
 
+bool app_exists(const char *app_id, const char *app_category)
+{
+    // Check if the app exists
+    char directory_path[128];
+    snprintf(directory_path, sizeof(directory_path), STORAGE_EXT_PATH_PREFIX "/apps/%s/%s.fap", app_category, app_id);
+
+    Storage *storage = furi_record_open(RECORD_STORAGE);
+    bool exists = storage_common_exists(storage, directory_path);
+    furi_record_close(RECORD_STORAGE);
+
+    return exists;
+}
+
 // Function to parse JSON incrementally from a file
 bool parse_json_incrementally(const char *file_path, const char *target_key, char *value_buffer, size_t value_buffer_size)
 {

+ 2 - 1
flip_storage/flip_store_storage.h

@@ -20,9 +20,10 @@ bool load_settings(
     char *password,
     size_t password_size);
 
-// future implenetation because we need the app category
 bool delete_app(const char *app_id, const char *app_category);
 
+bool app_exists(const char *app_id, const char *app_category);
+
 // Function to parse JSON incrementally from a file
 bool parse_json_incrementally(const char *file_path, const char *target_key, char *value_buffer, size_t value_buffer_size);
 

+ 70 - 42
flipper_http/flipper_http.c

@@ -2,6 +2,7 @@
 FlipperHTTP fhttp;
 char rx_line_buffer[RX_LINE_BUFFER_SIZE];
 uint8_t file_buffer[FILE_BUFFER_SIZE];
+size_t file_buffer_len = 0;
 // Function to append received data to file
 // make sure to initialize the file path before calling this function
 bool flipper_http_append_to_file(
@@ -147,14 +148,15 @@ int32_t flipper_http_worker(void *context)
 {
     UNUSED(context);
     size_t rx_line_pos = 0;
-    static size_t file_buffer_len = 0;
 
     while (1)
     {
         uint32_t events = furi_thread_flags_wait(
             WorkerEvtStop | WorkerEvtRxDone, FuriFlagWaitAny, FuriWaitForever);
         if (events & WorkerEvtStop)
+        {
             break;
+        }
         if (events & WorkerEvtRxDone)
         {
             // Continuously read from the stream buffer until it's empty
@@ -178,6 +180,7 @@ int32_t flipper_http_worker(void *context)
                     // Write to file if buffer is full
                     if (file_buffer_len >= FILE_BUFFER_SIZE)
                     {
+                        // remove [POST/END] and/or [GET/END] from the file
                         if (!flipper_http_append_to_file(
                                 file_buffer, file_buffer_len, false, fhttp.file_path))
                         {
@@ -210,45 +213,6 @@ int32_t flipper_http_worker(void *context)
         }
     }
 
-    if (fhttp.save_bytes)
-    {
-        // Write the remaining data to the file
-        if (file_buffer_len > 0)
-        {
-            if (!flipper_http_append_to_file(file_buffer, file_buffer_len, false, fhttp.file_path))
-            {
-                FURI_LOG_E(HTTP_TAG, "Failed to append remaining data to file");
-            }
-        }
-    }
-
-    // remove [POST/END] and/or [GET/END] from the file
-    if (fhttp.save_bytes)
-    {
-        char *end = NULL;
-        if ((end = strstr(fhttp.file_path, "[POST/END]")) != NULL)
-        {
-            *end = '\0';
-        }
-        else if ((end = strstr(fhttp.file_path, "[GET/END]")) != NULL)
-        {
-            *end = '\0';
-        }
-    }
-
-    // remove newline from the from the end of the file
-    if (fhttp.save_bytes)
-    {
-        char *end = NULL;
-        if ((end = strstr(fhttp.file_path, "\n")) != NULL)
-        {
-            *end = '\0';
-        }
-    }
-
-    // Reset the file buffer length
-    file_buffer_len = 0;
-
     return 0;
 }
 // Timer callback function
@@ -1128,8 +1092,39 @@ void flipper_http_rx_callback(const char *line, void *context)
             fhttp.just_started_get = false;
             fhttp.state = IDLE;
             fhttp.save_bytes = false;
-            fhttp.is_bytes_request = false;
             fhttp.save_received_data = false;
+
+            if (fhttp.is_bytes_request)
+            {
+                // Search for the binary marker `[GET/END]` in the file buffer
+                const char marker[] = "[GET/END]";
+                const size_t marker_len = sizeof(marker) - 1; // Exclude null terminator
+
+                for (size_t i = 0; i <= file_buffer_len - marker_len; i++)
+                {
+                    // Check if the marker is found
+                    if (memcmp(&file_buffer[i], marker, marker_len) == 0)
+                    {
+                        // Remove the marker by shifting the remaining data left
+                        size_t remaining_len = file_buffer_len - (i + marker_len);
+                        memmove(&file_buffer[i], &file_buffer[i + marker_len], remaining_len);
+                        file_buffer_len -= marker_len;
+                        break;
+                    }
+                }
+
+                // If there is data left in the buffer, append it to the file
+                if (file_buffer_len > 0)
+                {
+                    if (!flipper_http_append_to_file(file_buffer, file_buffer_len, false, fhttp.file_path))
+                    {
+                        FURI_LOG_E(HTTP_TAG, "Failed to append data to file.");
+                    }
+                    file_buffer_len = 0;
+                }
+            }
+
+            fhttp.is_bytes_request = false;
             return;
         }
 
@@ -1167,8 +1162,39 @@ void flipper_http_rx_callback(const char *line, void *context)
             fhttp.just_started_post = false;
             fhttp.state = IDLE;
             fhttp.save_bytes = false;
-            fhttp.is_bytes_request = false;
             fhttp.save_received_data = false;
+
+            if (fhttp.is_bytes_request)
+            {
+                // Search for the binary marker `[POST/END]` in the file buffer
+                const char marker[] = "[POST/END]";
+                const size_t marker_len = sizeof(marker) - 1; // Exclude null terminator
+
+                for (size_t i = 0; i <= file_buffer_len - marker_len; i++)
+                {
+                    // Check if the marker is found
+                    if (memcmp(&file_buffer[i], marker, marker_len) == 0)
+                    {
+                        // Remove the marker by shifting the remaining data left
+                        size_t remaining_len = file_buffer_len - (i + marker_len);
+                        memmove(&file_buffer[i], &file_buffer[i + marker_len], remaining_len);
+                        file_buffer_len -= marker_len;
+                        break;
+                    }
+                }
+
+                // If there is data left in the buffer, append it to the file
+                if (file_buffer_len > 0)
+                {
+                    if (!flipper_http_append_to_file(file_buffer, file_buffer_len, false, fhttp.file_path))
+                    {
+                        FURI_LOG_E(HTTP_TAG, "Failed to append data to file.");
+                    }
+                    file_buffer_len = 0;
+                }
+            }
+
+            fhttp.is_bytes_request = false;
             return;
         }
 
@@ -1291,6 +1317,7 @@ void flipper_http_rx_callback(const char *line, void *context)
         fhttp.state = RECEIVING;
         // for GET request, save data only if it's a bytes request
         fhttp.save_bytes = fhttp.is_bytes_request;
+        file_buffer_len = 0;
         return;
     }
     else if (strstr(line, "[POST/SUCCESS]") != NULL)
@@ -1301,6 +1328,7 @@ void flipper_http_rx_callback(const char *line, void *context)
         fhttp.state = RECEIVING;
         // for POST request, save data only if it's a bytes request
         fhttp.save_bytes = fhttp.is_bytes_request;
+        file_buffer_len = 0;
         return;
     }
     else if (strstr(line, "[PUT/SUCCESS]") != NULL)

+ 2 - 1
flipper_http/flipper_http.h

@@ -15,7 +15,7 @@
 #define UART_CH (FuriHalSerialIdUsart)    // UART channel
 #define TIMEOUT_DURATION_TICKS (5 * 1000) // 5 seconds
 #define BAUDRATE (115200)                 // UART baudrate
-#define RX_BUF_SIZE 1024                  // UART RX buffer size
+#define RX_BUF_SIZE 2048                  // UART RX buffer size
 #define RX_LINE_BUFFER_SIZE 8192          // UART RX line buffer size (increase for large responses)
 #define MAX_FILE_SHOW 8192                // Maximum data from file to show
 #define FILE_BUFFER_SIZE 512              // File buffer size
@@ -82,6 +82,7 @@ extern FlipperHTTP fhttp;
 // Global static array for the line buffer
 extern char rx_line_buffer[RX_LINE_BUFFER_SIZE];
 extern uint8_t file_buffer[FILE_BUFFER_SIZE];
+extern size_t file_buffer_len;
 
 // fhttp.last_response holds the last received data from the UART