latest change on laptop Bible and SDDownload
This commit is contained in:
@@ -21,6 +21,7 @@
|
||||
#define DEFAULT_PATH "/sdcard/download/test2.mp3"
|
||||
|
||||
typedef struct {
|
||||
lv_obj_t *root_obj;
|
||||
lv_obj_t *bar;
|
||||
lv_obj_t *lbl_url;
|
||||
lv_obj_t *lbl_path;
|
||||
@@ -37,6 +38,7 @@ typedef struct {
|
||||
TaskHandle_t dl_handle;
|
||||
volatile bool downloading;
|
||||
volatile bool cancel_req;
|
||||
volatile bool task_exited;
|
||||
volatile int total;
|
||||
volatile int expected;
|
||||
int last_pct;
|
||||
@@ -82,13 +84,18 @@ static void ensure_parent_dir(const char *filepath) {
|
||||
}
|
||||
}
|
||||
|
||||
#define DL_DOWNLOADED 0
|
||||
#define DL_EXISTED 1
|
||||
#define DL_FAILED -1
|
||||
|
||||
static void update_ui_status(const char *txt) {
|
||||
tt_lvgl_lock(portMAX_DELAY);
|
||||
if (G.lbl_status) lv_label_set_text(G.lbl_status, txt);
|
||||
tt_lvgl_unlock();
|
||||
}
|
||||
|
||||
static void set_result_and_close(bool success) {
|
||||
static void set_result_and_close(int dl_res) {
|
||||
bool success = (dl_res != DL_FAILED);
|
||||
G.result_success = success;
|
||||
size_t free_internal = heap_caps_get_free_size(MALLOC_CAP_INTERNAL);
|
||||
BundleHandle result_bundle = NULL;
|
||||
@@ -97,6 +104,13 @@ static void set_result_and_close(bool success) {
|
||||
}
|
||||
if (result_bundle) {
|
||||
tt_bundle_put_bool(result_bundle, "success", success);
|
||||
if (dl_res == DL_DOWNLOADED) {
|
||||
tt_bundle_put_string(result_bundle, "status", "downloaded");
|
||||
} else if (dl_res == DL_EXISTED) {
|
||||
tt_bundle_put_string(result_bundle, "status", "existed");
|
||||
} else {
|
||||
tt_bundle_put_string(result_bundle, "status", "failure");
|
||||
}
|
||||
// Keep bundle minimal to avoid low heap crash (previous put_string path 512B caused OOM)
|
||||
tt_app_set_result(G.app_handle, success ? APP_RESULT_OK : APP_RESULT_ERROR, result_bundle);
|
||||
} else {
|
||||
@@ -106,16 +120,9 @@ static void set_result_and_close(bool success) {
|
||||
}
|
||||
tt_app_set_result(G.app_handle, success ? APP_RESULT_OK : APP_RESULT_ERROR, NULL);
|
||||
}
|
||||
// Change Cancel button to Close once done (manual close, auto-close unreliable)
|
||||
tt_lvgl_lock(portMAX_DELAY);
|
||||
if (G.btn_cancel) {
|
||||
lv_obj_t* lbl = lv_obj_get_child(G.btn_cancel, 0);
|
||||
if (lbl) lv_label_set_text(lbl, "Close");
|
||||
}
|
||||
tt_lvgl_unlock();
|
||||
}
|
||||
|
||||
static bool download_one_file(const char *url, const char *path) {
|
||||
static int download_one_file(const char *url, const char *path) {
|
||||
ESP_LOGI(TAG, "DL one file url=%s path=%s override=%d", url, path, G.override_existing);
|
||||
|
||||
struct stat st;
|
||||
@@ -124,7 +131,7 @@ static bool download_one_file(const char *url, const char *path) {
|
||||
ESP_LOGW(TAG, "File exists and override=false: %s (returning true as cached)", path);
|
||||
G.total = st.st_size;
|
||||
update_ui_status("Already on SD");
|
||||
return true;
|
||||
return DL_EXISTED;
|
||||
} else {
|
||||
ESP_LOGI(TAG, "File exists but override=true, overwriting %s", path);
|
||||
unlink(path);
|
||||
@@ -141,7 +148,7 @@ static bool download_one_file(const char *url, const char *path) {
|
||||
|
||||
if (strlen(url) < 8 || strncmp(url, "http://", 7)!=0) {
|
||||
update_ui_status("Invalid URL");
|
||||
return false;
|
||||
return DL_FAILED;
|
||||
}
|
||||
|
||||
char tmp_path[520];
|
||||
@@ -151,7 +158,7 @@ static bool download_one_file(const char *url, const char *path) {
|
||||
if (!f) {
|
||||
ESP_LOGE(TAG, "fopen fail %s errno=%d", tmp_path, errno);
|
||||
update_ui_status("Failed to open file");
|
||||
return false;
|
||||
return DL_FAILED;
|
||||
}
|
||||
|
||||
esp_http_client_config_t cfg = {
|
||||
@@ -166,7 +173,7 @@ static bool download_one_file(const char *url, const char *path) {
|
||||
fclose(f);
|
||||
unlink(tmp_path);
|
||||
update_ui_status("HTTP init failed");
|
||||
return false;
|
||||
return DL_FAILED;
|
||||
}
|
||||
|
||||
esp_err_t err = esp_http_client_open(client, 0);
|
||||
@@ -175,7 +182,7 @@ static bool download_one_file(const char *url, const char *path) {
|
||||
fclose(f);
|
||||
unlink(tmp_path);
|
||||
update_ui_status("HTTP open failed");
|
||||
return false;
|
||||
return DL_FAILED;
|
||||
}
|
||||
|
||||
int content_len = esp_http_client_fetch_headers(client);
|
||||
@@ -189,7 +196,7 @@ static bool download_one_file(const char *url, const char *path) {
|
||||
esp_http_client_cleanup(client);
|
||||
fclose(f);
|
||||
unlink(tmp_path);
|
||||
return false;
|
||||
return DL_FAILED;
|
||||
}
|
||||
|
||||
G.expected = content_len;
|
||||
@@ -203,7 +210,7 @@ static bool download_one_file(const char *url, const char *path) {
|
||||
fclose(f);
|
||||
unlink(tmp_path);
|
||||
update_ui_status("No memory");
|
||||
return false;
|
||||
return DL_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -264,18 +271,19 @@ static bool download_one_file(const char *url, const char *path) {
|
||||
esp_http_client_cleanup(client);
|
||||
fclose(f);
|
||||
|
||||
if (G.cancel_req) { unlink(tmp_path); update_ui_status("Canceled"); return false; }
|
||||
if (!ok || total < 512) { unlink(tmp_path); update_ui_status("Failed – error"); return false; }
|
||||
if (content_len >=0 && total < content_len * 95 / 100) { unlink(tmp_path); update_ui_status("Failed – incomplete"); return false; }
|
||||
if (rename(tmp_path, path) != 0) { unlink(tmp_path); update_ui_status("Rename failed"); return false; }
|
||||
if (G.cancel_req) { unlink(tmp_path); update_ui_status("Canceled"); return DL_FAILED; }
|
||||
if (!ok || total < 512) { unlink(tmp_path); update_ui_status("Failed – error"); return DL_FAILED; }
|
||||
if (content_len >=0 && total < content_len * 95 / 100) { unlink(tmp_path); update_ui_status("Failed – incomplete"); return DL_FAILED; }
|
||||
if (rename(tmp_path, path) != 0) { unlink(tmp_path); update_ui_status("Rename failed"); return DL_FAILED; }
|
||||
|
||||
return true;
|
||||
return DL_DOWNLOADED;
|
||||
}
|
||||
|
||||
static void download_task(void *arg) {
|
||||
(void)arg;
|
||||
G.downloading = true;
|
||||
G.cancel_req = false;
|
||||
G.task_exited = false;
|
||||
G.last_pct = -1;
|
||||
|
||||
char url[512];
|
||||
@@ -285,35 +293,48 @@ static void download_task(void *arg) {
|
||||
url[sizeof(url)-1]=0;
|
||||
path[sizeof(path)-1]=0;
|
||||
|
||||
bool ok = download_one_file(url, path);
|
||||
if (ok) {
|
||||
int dl_res = download_one_file(url, path);
|
||||
if (dl_res == DL_DOWNLOADED || dl_res == DL_EXISTED) {
|
||||
char msg[64];
|
||||
snprintf(msg, sizeof(msg), "Done! %d KB", G.total/1024);
|
||||
if (dl_res == DL_EXISTED) snprintf(msg, sizeof(msg), "Already on SD (%d KB)", G.total/1024);
|
||||
else snprintf(msg, sizeof(msg), "Done! %d KB", G.total/1024);
|
||||
update_ui_status(msg);
|
||||
}
|
||||
|
||||
tt_lvgl_lock(portMAX_DELAY);
|
||||
if (G.btn_cancel) {
|
||||
lv_obj_t* lbl = lv_obj_get_child(G.btn_cancel, 0);
|
||||
if (lbl) lv_label_set_text(lbl, "Close");
|
||||
}
|
||||
tt_lvgl_unlock();
|
||||
|
||||
G.downloading = false;
|
||||
set_result_and_close(dl_res);
|
||||
G.task_exited = true;
|
||||
G.dl_handle = NULL;
|
||||
set_result_and_close(ok);
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
static void btn_cancel_cb(lv_event_t *e) {
|
||||
(void)e;
|
||||
ESP_LOGI(TAG, "btn_cancel_cb clicked! downloading=%d", G.downloading);
|
||||
if (G.btn_cancel) {
|
||||
lv_obj_remove_flag(G.btn_cancel, LV_OBJ_FLAG_CLICKABLE);
|
||||
}
|
||||
|
||||
if (!G.downloading) {
|
||||
tt_app_stop();
|
||||
return;
|
||||
}
|
||||
G.cancel_req = true;
|
||||
tt_lvgl_lock(portMAX_DELAY);
|
||||
if (G.lbl_status) lv_label_set_text(G.lbl_status, "Canceling...");
|
||||
tt_lvgl_unlock();
|
||||
}
|
||||
|
||||
static void build_ui(void) {
|
||||
tt_lvgl_lock(portMAX_DELAY);
|
||||
|
||||
lv_obj_t *root = lv_obj_create(lv_scr_act());
|
||||
G.root_obj = root;
|
||||
lv_obj_set_size(root, LV_PCT(100), LV_PCT(100));
|
||||
lv_obj_set_style_pad_all(root, 8, 0);
|
||||
lv_obj_set_style_bg_color(root, lv_color_hex(0x111111), 0);
|
||||
@@ -448,9 +469,31 @@ static void onHide(AppHandle app, void* data) {
|
||||
(void)app; (void)data;
|
||||
if (G.dl_handle) {
|
||||
G.cancel_req = true;
|
||||
for (int i=0;i<20 && G.dl_handle!=NULL; i++) vTaskDelay(pdMS_TO_TICKS(100));
|
||||
if (G.dl_handle) { vTaskDelete(G.dl_handle); G.dl_handle = NULL; G.downloading = false; }
|
||||
for (int i = 0; i < 30 && !G.task_exited; i++) {
|
||||
vTaskDelay(pdMS_TO_TICKS(100));
|
||||
}
|
||||
if (!G.task_exited && G.dl_handle) {
|
||||
ESP_LOGW(TAG, "onHide: force deleting dl task after timeout");
|
||||
vTaskDelete(G.dl_handle);
|
||||
G.dl_handle = NULL;
|
||||
G.downloading = false;
|
||||
}
|
||||
vTaskDelay(pdMS_TO_TICKS(50));
|
||||
}
|
||||
|
||||
tt_lvgl_lock(portMAX_DELAY);
|
||||
if (G.root_obj) {
|
||||
lv_obj_delete(G.root_obj);
|
||||
G.root_obj = NULL;
|
||||
G.lbl_url = NULL;
|
||||
G.lbl_path = NULL;
|
||||
G.lbl_pct = NULL;
|
||||
G.lbl_detail = NULL;
|
||||
G.lbl_status = NULL;
|
||||
G.bar = NULL;
|
||||
G.btn_cancel = NULL;
|
||||
}
|
||||
tt_lvgl_unlock();
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
|
||||
Reference in New Issue
Block a user