jblanked 9 месяцев назад
Родитель
Сommit
87a734ee7a
3 измененных файлов с 217 добавлено и 131 удалено
  1. 10 2
      app.c
  2. 202 128
      flip_world.c
  3. 5 1
      flip_world.h

+ 10 - 2
app.c

@@ -43,9 +43,8 @@ int32_t flip_world_main(void *p)
     while (fhttp->state == INACTIVE && --counter > 0)
     {
         FURI_LOG_D(TAG, "Waiting for PONG");
-        furi_delay_ms(100); // this causes a BusFault
+        furi_delay_ms(100);
     }
-    flipper_http_free(fhttp);
 
     if (counter == 0)
         easy_flipper_dialog("FlipperHTTP Error", "Ensure your WiFi Developer\nBoard or Pico W is connected\nand the latest FlipperHTTP\nfirmware is installed.");
@@ -55,6 +54,15 @@ int32_t flip_world_main(void *p)
     // snprintf(app_version, sizeof(app_version), "%f", (double)VERSION);
     save_char("app_version", VERSION);
 
+    // for now use the catalog API until I implement caching on the server
+
+    if (flip_world_handle_app_update(fhttp, true))
+    {
+        easy_flipper_dialog("Update Status", "Complete.\nRestart your Flipper Zero.");
+    }
+
+    flipper_http_free(fhttp);
+
     // Run the view dispatcher
     view_dispatcher_run(app->view_dispatcher);
 

+ 202 - 128
flip_world.c

@@ -36,58 +36,56 @@ bool is_enough_heap(size_t heap_size, bool check_blocks)
     return true;
 }
 
-static DateTime *flip_world_get_rtc_time()
+static bool flip_world_json_to_datetime(DateTime *rtc_time, FuriString *str)
 {
-    DateTime *rtc_time = malloc(sizeof(DateTime));
-    if (!rtc_time)
+    if (!rtc_time || !str)
     {
-        FURI_LOG_E(TAG, "Failed to allocate memory for DateTime");
-        return NULL;
+        FURI_LOG_E(TAG, "rtc_time or str is NULL");
+        return false;
     }
-    furi_hal_rtc_get_datetime(rtc_time);
-    return rtc_time;
-}
-
-static DateTime *flip_world_json_to_datetime(const char *str)
-{
-    DateTime *rtc_time = malloc(sizeof(DateTime));
-    if (!rtc_time)
+    FuriString *hour = get_json_value_furi("hour", str);
+    if (hour)
     {
-        FURI_LOG_E(TAG, "Failed to allocate memory for DateTime");
-        return NULL;
+        rtc_time->hour = atoi(furi_string_get_cstr(hour));
+        furi_string_free(hour);
     }
-    char *hour = get_json_value("hour", str);
-    char *minute = get_json_value("minute", str);
-    char *second = get_json_value("second", str);
-    char *day = get_json_value("day", str);
-    char *month = get_json_value("month", str);
-    char *year = get_json_value("year", str);
-    char *weekday = get_json_value("weekday", str);
-
-    if (!hour || !minute || !second || !day || !month || !year || !weekday)
+    FuriString *minute = get_json_value_furi("minute", str);
+    if (minute)
     {
-        FURI_LOG_E(TAG, "Failed to parse JSON");
-        free(rtc_time);
-        return NULL;
+        rtc_time->minute = atoi(furi_string_get_cstr(minute));
+        furi_string_free(minute);
     }
-
-    rtc_time->hour = atoi(hour);
-    rtc_time->minute = atoi(minute);
-    rtc_time->second = atoi(second);
-    rtc_time->day = atoi(day);
-    rtc_time->month = atoi(month);
-    rtc_time->year = atoi(year);
-    rtc_time->weekday = atoi(weekday);
-
-    free(hour);
-    free(minute);
-    free(second);
-    free(day);
-    free(month);
-    free(year);
-    free(weekday);
-
-    return rtc_time;
+    FuriString *second = get_json_value_furi("second", str);
+    if (second)
+    {
+        rtc_time->second = atoi(furi_string_get_cstr(second));
+        furi_string_free(second);
+    }
+    FuriString *day = get_json_value_furi("day", str);
+    if (day)
+    {
+        rtc_time->day = atoi(furi_string_get_cstr(day));
+        furi_string_free(day);
+    }
+    FuriString *month = get_json_value_furi("month", str);
+    if (month)
+    {
+        rtc_time->month = atoi(furi_string_get_cstr(month));
+        furi_string_free(month);
+    }
+    FuriString *year = get_json_value_furi("year", str);
+    if (year)
+    {
+        rtc_time->year = atoi(furi_string_get_cstr(year));
+        furi_string_free(year);
+    }
+    FuriString *weekday = get_json_value_furi("weekday", str);
+    if (weekday)
+    {
+        rtc_time->weekday = atoi(furi_string_get_cstr(weekday));
+        furi_string_free(weekday);
+    }
+    return datetime_validate_datetime(rtc_time);
 }
 
 static FuriString *flip_world_datetime_to_json(DateTime *rtc_time)
@@ -97,14 +95,10 @@ static FuriString *flip_world_datetime_to_json(DateTime *rtc_time)
         FURI_LOG_E(TAG, "rtc_time is NULL");
         return NULL;
     }
-    FuriString *json = furi_string_alloc();
-    if (!json)
-    {
-        FURI_LOG_E(TAG, "Failed to allocate memory for JSON");
-        return NULL;
-    }
-    furi_string_printf(
+    char json[256];
+    snprintf(
         json,
+        sizeof(json),
         "{\"hour\":%d,\"minute\":%d,\"second\":%d,\"day\":%d,\"month\":%d,\"year\":%d,\"weekday\":%d}",
         rtc_time->hour,
         rtc_time->minute,
@@ -113,7 +107,7 @@ static FuriString *flip_world_datetime_to_json(DateTime *rtc_time)
         rtc_time->month,
         rtc_time->year,
         rtc_time->weekday);
-    return json;
+    return furi_string_alloc_set_str(json);
 }
 
 static bool flip_world_save_rtc_time(DateTime *rtc_time)
@@ -144,7 +138,7 @@ static bool flip_world_is_update_time(DateTime *time_current)
         FURI_LOG_E(TAG, "time_current is NULL");
         return false;
     }
-    char last_updated_old[32];
+    char last_updated_old[128];
     if (!load_char("last_updated", last_updated_old, sizeof(last_updated_old)))
     {
         FURI_LOG_E(TAG, "Failed to load last_updated");
@@ -156,18 +150,28 @@ static bool flip_world_is_update_time(DateTime *time_current)
         }
         return false;
     }
-    DateTime *last_updated_time = flip_world_json_to_datetime(last_updated_old);
-    if (!last_updated_time)
+
+    DateTime last_updated_time;
+
+    FuriString *last_updated_furi = char_to_furi_string(last_updated_old);
+    if (!last_updated_furi)
+    {
+        FURI_LOG_E(TAG, "Failed to convert char to FuriString");
+        return false;
+    }
+    if (!flip_world_json_to_datetime(&last_updated_time, last_updated_furi))
     {
         FURI_LOG_E(TAG, "Failed to convert JSON to DateTime");
+        furi_string_free(last_updated_furi);
         return false;
     }
+    furi_string_free(last_updated_furi); // Free after usage.
 
     bool time_diff = false;
     // If the date is different assume more than one hour has passed.
-    if (time_current->year != last_updated_time->year ||
-        time_current->month != last_updated_time->month ||
-        time_current->day != last_updated_time->day)
+    if (time_current->year != last_updated_time.year ||
+        time_current->month != last_updated_time.month ||
+        time_current->day != last_updated_time.day)
     {
         time_diff = true;
     }
@@ -175,23 +179,13 @@ static bool flip_world_is_update_time(DateTime *time_current)
     {
         // For the same day, compute seconds from midnight.
         int seconds_current = time_current->hour * 3600 + time_current->minute * 60 + time_current->second;
-        int seconds_last = last_updated_time->hour * 3600 + last_updated_time->minute * 60 + last_updated_time->second;
+        int seconds_last = last_updated_time.hour * 3600 + last_updated_time.minute * 60 + last_updated_time.second;
         if ((seconds_current - seconds_last) >= 3600)
         {
             time_diff = true;
         }
     }
 
-    if (time_diff)
-    {
-        FuriString *json = flip_world_datetime_to_json(time_current);
-        if (json)
-        {
-            save_char("last_updated", furi_string_get_cstr(json));
-            furi_string_free(json);
-        }
-    }
-    free(last_updated_time);
     return time_diff;
 }
 
@@ -203,17 +197,38 @@ static bool flip_world_last_app_update(FlipperHTTP *fhttp, bool flipper_server)
         FURI_LOG_E(TAG, "fhttp is NULL");
         return false;
     }
-    return flipper_http_request(
-        fhttp,
-        GET,
-        !flipper_server ? "https://www.jblanked.com/flipper/api/app/last-updated/flip_world/"
-                        : "//--TODO--//",
-        "{\"Content-Type\":\"application/json\"}",
-        NULL);
+    char url[256];
+    if (flipper_server)
+    {
+        // make sure folder is created
+        char directory_path[256];
+        snprintf(directory_path, sizeof(directory_path), STORAGE_EXT_PATH_PREFIX "/apps_data/flip_world");
+
+        // Create the directory
+        Storage *storage = furi_record_open(RECORD_STORAGE);
+        storage_common_mkdir(storage, directory_path);
+        snprintf(directory_path, sizeof(directory_path), STORAGE_EXT_PATH_PREFIX "/apps_data/flip_world/data");
+        storage_common_mkdir(storage, directory_path);
+        snprintf(directory_path, sizeof(directory_path), STORAGE_EXT_PATH_PREFIX "/apps_data/flip_world/data/last_update_request.txt");
+        storage_simply_remove_recursive(storage, directory_path); // ensure the file is empty
+        furi_record_close(RECORD_STORAGE);
+
+        fhttp->save_received_data = true;
+        fhttp->is_bytes_request = false;
+
+        snprintf(fhttp->file_path, sizeof(fhttp->file_path), STORAGE_EXT_PATH_PREFIX "/apps_data/flip_world/data/last_update_request.txt");
+        snprintf(url, sizeof(url), "https://catalog.flipperzero.one/api/v0/0/application/%s?is_latest_release_version=true", BUILD_ID);
+        return flipper_http_request(fhttp, GET, url, "{\"Content-Type\":\"application/json\"}", NULL);
+    }
+    else
+    {
+        snprintf(url, sizeof(url), "https://www.jblanked.com/flipper/api/app/last-updated/flip_world/");
+        return flipper_http_request(fhttp, GET, url, "{\"Content-Type\":\"application/json\"}", NULL);
+    }
 }
 
 // Parses the server response and returns true if an update is available.
-static bool flip_world_parse_last_app_update(FlipperHTTP *fhttp, DateTime *time_current)
+static bool flip_world_parse_last_app_update(FlipperHTTP *fhttp, DateTime *time_current, bool flipper_server)
 {
     if (!fhttp)
     {
@@ -225,15 +240,60 @@ static bool flip_world_parse_last_app_update(FlipperHTTP *fhttp, DateTime *time_
         FURI_LOG_E(TAG, "Failed to fetch last app update");
         return false;
     }
-    if (fhttp->last_response == NULL || strlen(fhttp->last_response) == 0)
+    char version_str[32];
+    if (!flipper_server)
     {
-        FURI_LOG_E(TAG, "fhttp->last_response is NULL or empty");
-        return false;
-    }
-
-    // Save the server app version.
-    save_char("server_app_version", fhttp->last_response);
+        if (fhttp->last_response == NULL || strlen(fhttp->last_response) == 0)
+        {
+            FURI_LOG_E(TAG, "fhttp->last_response is NULL or empty");
+            return false;
+        }
 
+        char *app_version = get_json_value("version", fhttp->last_response);
+        if (app_version)
+        {
+            // Save the server app version: it should save something like: 0.8
+            save_char("server_app_version", app_version);
+            snprintf(version_str, sizeof(version_str), "%s", app_version);
+            free(app_version);
+        }
+        else
+        {
+            FURI_LOG_E(TAG, "Failed to get app version");
+            return false;
+        }
+    }
+    else
+    {
+        FuriString *app_data = flipper_http_load_from_file_with_limit(fhttp->file_path, memmgr_heap_get_max_free_block());
+        if (!app_data)
+        {
+            FURI_LOG_E(TAG, "Failed to load app data");
+            return false;
+        }
+        FuriString *current_version = get_json_value_furi("current_version", app_data);
+        if (!current_version)
+        {
+            FURI_LOG_E(TAG, "Failed to get current version");
+            furi_string_free(app_data);
+            return false;
+        }
+        furi_string_free(app_data);
+        FuriString *version = get_json_value_furi("version", current_version);
+        if (!version)
+        {
+            FURI_LOG_E(TAG, "Failed to get version");
+            furi_string_free(current_version);
+            furi_string_free(app_data);
+            return false;
+        }
+        // Save the server app version: it should save something like: 0.8
+        save_char("server_app_version", furi_string_get_cstr(version));
+        snprintf(version_str, sizeof(version_str), "%s", furi_string_get_cstr(version));
+        furi_string_free(current_version);
+        furi_string_free(version);
+        // furi_string_free(app_data);
+    }
     // Only check for an update if an hour or more has passed.
     if (flip_world_is_update_time(time_current))
     {
@@ -243,17 +303,18 @@ static bool flip_world_parse_last_app_update(FlipperHTTP *fhttp, DateTime *time_
             FURI_LOG_E(TAG, "Failed to load app version");
             return false;
         }
-        if (strcmp(fhttp->last_response, app_version) != 0)
+        FURI_LOG_I(TAG, "App version: %s", app_version);
+        FURI_LOG_I(TAG, "Server version: %s", version_str);
+        // Check if the app version is different from the server version.
+        if (!is_str(app_version, version_str))
         {
-            FURI_LOG_I(TAG, "Update available");
-            return true;
-        }
-        else
-        {
-            FURI_LOG_I(TAG, "No update available");
-            return false;
+            easy_flipper_dialog("Update available", "New update available!\nPress BACK to download.");
+            return true; // Update available.
         }
+        FURI_LOG_I(TAG, "No update available");
+        return false; // No update available.
     }
+    FURI_LOG_I(TAG, "Not enough time has passed since the last update check");
     return false; // Not yet time to update.
 }
 
@@ -281,7 +342,7 @@ static bool flip_world_get_fap_file(FlipperHTTP *fhttp, bool flipper_server)
     if (flipper_server)
     {
         char build_id[32];
-        snprintf(build_id, sizeof(build_id), "%s", "67f22eb325a4a6f1fb4a2c5d");
+        snprintf(build_id, sizeof(build_id), "%s", BUILD_ID);
         uint8_t target;
         target = furi_hal_version_get_hw_target();
         uint16_t api_major, api_minor;
@@ -297,90 +358,103 @@ static bool flip_world_get_fap_file(FlipperHTTP *fhttp, bool flipper_server)
     }
     else
     {
-        snprintf(url, sizeof(url), "https://www.jblanked.com/flipper/api/app/download/flip_world");
+        snprintf(url, sizeof(url), "https://www.jblanked.com/flipper/api/app/download/flip_world/");
     }
     return flipper_http_request(fhttp, BYTES, url, "{\"Content-Type\": \"application/octet-stream\"}", NULL);
 }
 
 // Updates the app. Uses the supplied current time for validating if update check should proceed.
-static void flip_world_update_app(FlipperHTTP *fhttp, DateTime *time_current)
+static bool flip_world_update_app(FlipperHTTP *fhttp, DateTime *time_current, bool use_flipper_api)
 {
     if (!fhttp)
     {
         FURI_LOG_E(TAG, "fhttp is NULL");
-        return;
+        return false;
     }
-    if (!flip_world_last_app_update(fhttp, false))
+    if (!flip_world_last_app_update(fhttp, use_flipper_api))
     {
         FURI_LOG_E(TAG, "Failed to fetch last app update");
-        return;
+        return false;
     }
+    fhttp->state = RECEIVING;
+    furi_timer_start(fhttp->get_timeout_timer, TIMEOUT_DURATION_TICKS);
     while (fhttp->state == RECEIVING && furi_timer_is_running(fhttp->get_timeout_timer) > 0)
     {
-        // Wait for the request to finish.
         furi_delay_ms(100);
     }
     furi_timer_stop(fhttp->get_timeout_timer);
-    if (flip_world_parse_last_app_update(fhttp, time_current))
+    if (flip_world_parse_last_app_update(fhttp, time_current, use_flipper_api))
     {
-        easy_flipper_dialog("App Update", "FlipWorld update is available.\nPress BACK to install.");
         if (!flip_world_get_fap_file(fhttp, false))
         {
-            FURI_LOG_E(TAG, "Failed to fetch fap file");
-            return;
+            FURI_LOG_E(TAG, "Failed to fetch fap file 1");
+            return false;
         }
-        while (fhttp->state == RECEIVING && furi_timer_is_running(fhttp->get_timeout_timer) > 0)
+        fhttp->state = RECEIVING;
+
+        while (fhttp->state == RECEIVING)
         {
             furi_delay_ms(100);
         }
-        furi_timer_stop(fhttp->get_timeout_timer);
+
         if (fhttp->state == ISSUE)
         {
-            FURI_LOG_E(TAG, "Failed to fetch fap file");
-            return;
+            FURI_LOG_E(TAG, "Failed to fetch fap file 2");
+            return false;
         }
-        easy_flipper_dialog("Update Done", "FlipWorld update is done.\nPress BACK to restart.");
-    }
-    else
-    {
-        FURI_LOG_I(TAG, "No update available");
+        return true;
     }
+
+    FURI_LOG_I(TAG, "No update available");
+    return false; // No update available.
 }
 
 // Handles the app update routine. This function obtains the current RTC time,
 // checks the "last_checked" value, and if it is more than one hour old, calls for an update.
-bool flip_world_handle_app_update(FlipperHTTP *fhttp)
+bool flip_world_handle_app_update(FlipperHTTP *fhttp, bool use_flipper_api)
 {
     if (!fhttp)
     {
         FURI_LOG_E(TAG, "fhttp is NULL");
         return false;
     }
-    DateTime *rtc_time = flip_world_get_rtc_time();
-    if (!rtc_time)
-    {
-        FURI_LOG_E(TAG, "Failed to get RTC time");
-        return false;
-    }
+    DateTime rtc_time;
+    furi_hal_rtc_get_datetime(&rtc_time);
     char last_checked[32];
     if (!load_char("last_checked", last_checked, sizeof(last_checked)))
     {
         // First time – save the current time and check for an update.
-        flip_world_save_rtc_time(rtc_time);
-        flip_world_update_app(fhttp, rtc_time);
-        free(rtc_time);
-        return true;
+        if (!flip_world_save_rtc_time(&rtc_time))
+        {
+            FURI_LOG_E(TAG, "Failed to save RTC time");
+            return false;
+        }
+        return flip_world_update_app(fhttp, &rtc_time, use_flipper_api);
     }
     else
     {
         // Check if the current RTC time is at least one hour past the stored time.
-        if (flip_world_is_update_time(rtc_time))
+        if (flip_world_is_update_time(&rtc_time))
         {
-            flip_world_update_app(fhttp, rtc_time);
-            free(rtc_time);
+            if (!flip_world_update_app(fhttp, &rtc_time, use_flipper_api))
+            {
+                FURI_LOG_E(TAG, "Failed to update app");
+                // save the current time for the next check.
+                if (!flip_world_save_rtc_time(&rtc_time))
+                {
+                    FURI_LOG_E(TAG, "Failed to save RTC time");
+                    return false;
+                }
+                return false;
+            }
+            // Save the current time for the next check.
+            if (!flip_world_save_rtc_time(&rtc_time))
+            {
+                FURI_LOG_E(TAG, "Failed to save RTC time");
+                return false;
+            }
             return true;
         }
-        free(rtc_time);
         return false; // No update necessary.
     }
 }

+ 5 - 1
flip_world.h

@@ -17,6 +17,10 @@
 #define TAG "FlipWorld"
 #define VERSION "0.8.1"
 #define VERSION_TAG TAG " " FAP_VERSION
+//
+
+#define APP_ID "67f22e9a25a4a6f1fb4a2c4a"
+#define BUILD_ID "676900d983aa88302bc114c6"
 
 // Define the submenu items for our FlipWorld application
 typedef enum
@@ -107,4 +111,4 @@ float atof_(const char *nptr);
 float atof_furi(const FuriString *nptr);
 bool is_str(const char *src, const char *dst);
 bool is_enough_heap(size_t heap_size, bool check_blocks);
-bool flip_world_handle_app_update(FlipperHTTP *fhttp);
+bool flip_world_handle_app_update(FlipperHTTP *fhttp, bool use_flipper_api);