优化表情下载功能,添加通知ui切换slice消息id
authorluozheng <1732036298@qq.com>
Mon, 29 Sep 2025 09:24:53 +0000 (17:24 +0800)
committerluozheng <1732036298@qq.com>
Mon, 29 Sep 2025 09:24:53 +0000 (17:24 +0800)
code/sdk/application/wearable/ln/nativeapp/nativeui/include/uiservice/ui_service.h
code/sdk/application/wearable/ln/nativeapp/nativeui/uiservice/src/ui_service.cpp
code/sdk/zhihui/leiniao/imsl_ln/leiniao_api_at.c
code/sdk/zhihui/leiniao/imsl_ln/ln_api.c
code/sdk/zhihui/leiniao/imsl_ln/ln_api.h
code/sdk/zhihui/leiniao/imsl_ln/ln_api_device_info.c
code/sdk/zhihui/leiniao/imsl_ln/ln_api_emoji.c
code/sdk/zhihui/leiniao/ln_api_cpp/ln_api_interface.cpp
code/sdk/zhihui/leiniao/ln_api_cpp/ln_api_interface.h

index 1c8fbc3..4c42149 100644 (file)
@@ -60,8 +60,9 @@ typedef enum {
     HAND_CHAT_STAMINA_NOTIFY, // 对话体力与流量通知
     HAND_API_ERROR_NOTIFY, // 协议错误通知
     HAND_BIND_AND_UNBIND_NOTIFY, // 绑定和解绑通知
-    HAND_SCREEN_IMAGE_DOWNLOAD_SUCCESS_NOTIFY, // 屏保下载成功通知
     HAND_TEXT_UPDATE_NOTIFY, // 更新文本内容通知
+    HAND_DOWNLOAD_FILE_NOTIFY, // 下载文件通知
+    HAND_DOWNLOAD_SUCCESS_NOTIFY, // 下载成功通知
     MAX_NUM_HANDLER, 
 } UiServiceMsgId;
 
index 4d542c8..c858f15 100644 (file)
@@ -96,8 +96,9 @@ static void HandChatStaminaNotify(Request *request);
 static void HandDownloadEmojiNotify(Request *request);
 static void HandApiErrorNotify(Request *request);
 static void HandBindAndUnbindNotify(Request *request);
-static void HandScreenImageDownloadSuccessNotify(Request *request);
 static void HandTextUpdateNotify(Request *request);
+static void HandDownloadFileNotify(Request *request);
+static void HandDownloadSuccessNotify(Request *request);
 // 消息处理程序项数组(每个消息类型对应一个处理函数,函数名以 Handle 开头)
 MsgHandlerEntry msgHandlers[] = {
     {HANDLE_JUMP_OTA_VIEW, HandleJumpOtaView},
@@ -110,8 +111,9 @@ MsgHandlerEntry msgHandlers[] = {
     {HAND_DOWNLOAD_EMOJI_NOTIFY,HandDownloadEmojiNotify},
     {HAND_API_ERROR_NOTIFY,HandApiErrorNotify},
     {HAND_BIND_AND_UNBIND_NOTIFY,HandBindAndUnbindNotify},
-    {HAND_SCREEN_IMAGE_DOWNLOAD_SUCCESS_NOTIFY,HandScreenImageDownloadSuccessNotify},
     {HAND_TEXT_UPDATE_NOTIFY,HandTextUpdateNotify},
+    {HAND_DOWNLOAD_FILE_NOTIFY,HandDownloadFileNotify},
+    {HAND_DOWNLOAD_SUCCESS_NOTIFY,HandDownloadSuccessNotify},
 };
 /* -------------------------------------------------------------------------- */
 void ScreenTurnOn(int32_t timeout)
@@ -534,14 +536,6 @@ void HandBindAndUnbindNotify(Request *request)
         std::bind(&OHOS::ThunderBirdModel::HandleBindAndUnbind,OHOS::ThunderBirdModel::GetInstance(), is_bind));
 }
 
-// 屏保下载成功通知
-void HandScreenImageDownloadSuccessNotify(Request *request)
-{
-    WEARABLE_LOGE(WEARABLE_LOG_MODULE_UI_SERVICE, "HandScreenImageDownloadSuccessNotify msg_id = %d", request->msgId);
-    // GraphicService::GetInstance()->PostGraphicEvent(
-    //     std::bind(&OHOS::ThunderBirdModel::HandleScreenImageDownloadSuccess,OHOS::ThunderBirdModel::GetInstance()));
-}
-
 // 更新文本内容通知
 void HandTextUpdateNotify(Request *request)
 {
@@ -550,6 +544,24 @@ void HandTextUpdateNotify(Request *request)
         std::bind(&OHOS::ThunderBirdModel::SetTextStr,OHOS::ThunderBirdModel::GetInstance(),value.c_str()));
 }
 
+// 下载文件通知
+void HandDownloadFileNotify(Request *request)
+{
+    WEARABLE_LOGE(WEARABLE_LOG_MODULE_UI_SERVICE, "HandDownloadFileNotify msg_id = %d", request->msgId);
+    int type = *(int *)request->data;
+    WEARABLE_LOGE(WEARABLE_LOG_MODULE_UI_SERVICE, "HandDownloadFileNotify type = %d", type);
+    // GraphicService::GetInstance()->PostGraphicEvent(
+    //     std::bind(&OHOS::ThunderBirdModel::HandleDownloadFile,OHOS::ThunderBirdModel::GetInstance(), type));
+}
+// 下载成功通知
+void HandDownloadSuccessNotify(Request *request)
+{
+    WEARABLE_LOGE(WEARABLE_LOG_MODULE_UI_SERVICE, "HandDownloadSuccessNotify msg_id = %d", request->msgId);
+    int type = *(int *)request->data;
+    WEARABLE_LOGE(WEARABLE_LOG_MODULE_UI_SERVICE, "HandDownloadSuccessNotify type = %d", type);
+    // GraphicService::GetInstance()->PostGraphicEvent(
+    //     std::bind(&OHOS::ThunderBirdModel::HandleDownloadSuccess,OHOS::ThunderBirdModel::GetInstance(), type));
+}
 /* -------------------------------------------------------------------------- */
 #ifdef __cplusplus
 }
index 3cbb8da..5fb801c 100644 (file)
@@ -1770,14 +1770,31 @@ static uint32_t ln_api_error_at(uint8_t *para, uint32_t paraLen, char *strCommon
     return ERRCODE_SUCC;
 }
 
-static uint32_t ln_notify_screen_image_at(uint8_t *para, uint32_t paraLen, char *strCommonCmd)
+//AT^LN_NOTIFY_DOWNLOAD_SUCCESS=0/1
+static uint32_t ln_download_success_notification_at(uint8_t *para, uint32_t paraLen, char *strCommonCmd)
 {
-    VAD_UNUSED(para);
-    VAD_UNUSED(paraLen);
     VAD_UNUSED(strCommonCmd);
-    push_screen_image_download_success_notification();
-    printf("+LN_NOTIFY_SCREEN_IMAGE: OK\n");
-    printf("[LN][NOTIFY_SCREEN_IMAGE] ok\n");
+    int type = 0;
+    uint8_t argv[LN_MAX_ARGC][LN_MAX_ARGV_LEN] = {0};
+    uint32_t argc = 0;
+
+    if (para != NULL && paraLen > 0) {
+        if (ConvertCmdToParam(para, paraLen, argv, &argc) != VAD_SUCCESS) {
+            printf("ERROR: Failed to parse parameters\n");
+            return ERRCODE_FAIL;
+        }
+        if (argc >= 1) {
+            type = atoi((char*)argv[0]);
+        }
+    }
+
+    if (type != 0 && type != 1) {
+        printf("ERROR: Type too long (max 1 characters)\n");
+        return ERRCODE_FAIL;
+    }
+    push_download_success_notification(type);
+    printf("+LN_NOTIFY_DOWNLOAD_SUCCESS: OK\n");
+    printf("[LN][NOTIFY_DOWNLOAD_SUCCESS] ok\n");
     return ERRCODE_SUCC;
 }
 
@@ -1844,7 +1861,7 @@ static const at_cmd_table_t g_ln_api_cmd[] = {
     {(char *)"AT^LN_SWITCH_EXPRESSION", ln_switch_expression_at, (char *)"切换表情视频: AT^LN_SWITCH_EXPRESSION=<video_path>"},
     {(char *)"AT^LN_API_ERROR", ln_api_error_at, (char *)"推送协议错误: AT^LN_API_ERROR[=message]"},
     {(char *)"AT^LN_NOTIFY_BIND_AND_UNBIND", ln_notify_bind_and_unbind_at, (char *)"通知用户绑定或解绑: AT^LN_NOTIFY_BIND_AND_UNBIND=1/0"},
-    {(char *)"AT^LN_NOTIFY_SCREEN_IMAGE", ln_notify_screen_image_at, (char *)"通知切换屏保: AT^LN_NOTIFY_SCREEN_IMAGE"},
+    {(char *)"AT^LN_NOTIFY_DOWNLOAD_SUCCESS", ln_download_success_notification_at, (char *)"通知切换屏保: AT^LN_NOTIFY_DOWNLOAD_SUCCESS=0/1"},
     
     
     /* 内存查看相关AT命令 */
index f6df7e5..332782c 100644 (file)
@@ -330,6 +330,13 @@ const char *uapi_ln_get_user_id(void)
     return g_ln_device_user_id;
 }
 
+/* 获取当前是否正在下载文件 */
+int uapi_ln_is_file_downloading(void)
+{
+    return g_ln_is_file_downloading;
+}
+
+
 /* 下载文件
 @param url 文件URL
 @param save_path 保存路径
@@ -421,51 +428,62 @@ int uapi_ln_download_file(const char *url, const char *save_path)
         }
         else
         {
-            WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD] recv_res = %d, break", recv_res);
+            WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD] 接收响应失败, break, recv_res = %d", recv_res);
             break;
         }
     } while (client_data.isMore);
 
     if (client_data.responseContentLen == 0)
     {
-        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD] receive length = 0");
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD] 接收长度为0");
     }
     WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD] responsecode = %d", client.responseCode);
     if (total_written == client_data.responseContentLen && client.responseCode == 200 && total_written > 0)
     {
         ret = ERRCODE_SUCC;
-        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD] The file is downloaded successfully and the data is complete!");
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD] 文件下载成功");
     }
     else
     {
         ret = ERRCODE_FAIL;
-        unlink(save_path); // 删除不完整文件
-        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD] The file data is incomplete!");
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD] 文件下载失败删除不完整文件,save_path = %s", save_path);
+        if (unlink(save_path) < 0)
+        {
+            WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "删除不完整文件失败\r\n");
+        }
+        else
+        {
+            WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "删除不完整文件成功\r\n");
+        }
     }
 
     HttpClientUnprepare(&client_data);
     HttpClientClse(&client);
     fclose(fd);
     g_ln_is_file_downloading = 0;
-    WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD] download process finished,ret=%d", ret);
+    WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD] 下载过程结束,ret=%d", ret);
     return ret;
 }
 
 /* 下载函数封装:带重试,不做UI通知与资源释放 */
 int uapi_ln_download_with_retry(const char *url, const char *save_path, int max_retries)
 {
-    if (url == NULL || save_path == NULL) {
-        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD] invalid params");
+    if (url == NULL || save_path == NULL)
+    {
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD] 参数无效");
         return ERRCODE_FAIL;
     }
-    WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD] start: %s -> %s", url, save_path);
+    WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD] 开始下载: %s -> %s", url, save_path);
     int ret = uapi_ln_download_file(url, save_path);
-    if (ERRCODE_SUCC != ret) {
-        for (int i = 1; i <= max_retries; i++) {
-            WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD] retry %d/%d", i, max_retries);
+    if (ERRCODE_SUCC != ret)
+    {
+        for (int i = 1; i <= max_retries; i++)
+        {
+            WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD] 重试 %d/%d", i, max_retries);
             ret = uapi_ln_download_file(url, save_path);
-            if (ERRCODE_SUCC == ret) {
-                WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD] success on retry %d", i);
+            if (ERRCODE_SUCC == ret)
+            {
+                WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD] 重试成功 %d", i);
                 break;
             }
         }
@@ -594,7 +612,7 @@ void ln_ws_process_message(const ws_data_msg_t *data_msg, ws_command_t type)
         printf("[WS] DownloadEmoji:下载表情\n");
         if (json_str)
         {
-            // ln_parse_download_emoji(json_str, (int)strlen(json_str));
+            ln_parse_download_emoji(json_str, (int)strlen(json_str));
         }
         break;
     }
@@ -1187,6 +1205,19 @@ static void ln_http_server_send_task(void const *arg)
             printf("LN_HTTP_SERVICE_CMD_OTA_DOWNLOAD:下载OTA升级包\n");
             ln_ota_download_flow_http(uapi_ln_get_device_id(), uapi_ln_get_device_token(), uapi_ln_get_device_version());
         }
+        else if (mail.cmd == LN_HTTP_SERVICE_CMD_DOWNLOAD_EMOJI)
+        {
+            WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "LN_HTTP_SERVICE_CMD_DOWNLOAD_EMOJI:下载表情\n");
+            ln_start_emoji_download();
+        }
+        else if (mail.cmd == LN_HTTP_SERVICE_CMD_DELETE_EMOJI)
+        {
+            WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "LN_HTTP_SERVICE_CMD_DELETE_EMOJI:删除表情\n");
+            if (mail.data)
+            {
+                // ln_delete_emoji_http(mail.data, mail.len);
+            }
+        }
         else if (mail.cmd == LN_HTTP_SERVICE_CMD_EMOJI_REPORT)
         {
             printf("LN_HTTP_SERVICE_CMD_EMOJI_REPORT:上报角色表情组下载状态\n");
@@ -1212,7 +1243,7 @@ static void ln_http_server_send_task(void const *arg)
                 if (ret == ERRCODE_SUCC)
                 {
                     WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DEVICE_CHARACTER] 屏保下载成功,通知去切换屏保");
-                    push_screen_image_download_success_notification();
+                    push_download_success_notification(DOWNLOAD_FILE_TYPE_SCREEN_IMAGE);
                 }
                 else
                 {
@@ -1233,7 +1264,7 @@ static void ln_http_server_send_task(void const *arg)
         }
         else if (mail.cmd == LN_HTTP_SERVICE_CMD_SCHEDULE_REMINDER)
         {
-            printf("LN_HTTP_SERVICE_CMD_SCHEDULE_REMINDER:日程提醒\n");
+            WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[LN_HTTP_SERVICE_CMD_SCHEDULE_REMINDER] 日程提醒");
             ln_parse_schedule_reminder(mail.data, mail.len);
         }
         if (mail.data != NULL)
index 51aed1d..3f4515c 100644 (file)
@@ -114,6 +114,10 @@ typedef struct
 #define LN_HTTP_SERVICE_CMD_DOWNLOAD_SCREEN_IMAGE 0x1009 /* 下载屏保文件 */
 #define LN_HTTP_SERVICE_CMD_SCHEDULE_REMINDER 0x1010     /* 日程提醒 */
 
+// 表情下载相关命令
+#define LN_HTTP_SERVICE_CMD_DOWNLOAD_EMOJI 0x1011 /* 下载表情 */
+#define LN_HTTP_SERVICE_CMD_DELETE_EMOJI 0x1012 /* 删除表情 */
+
 /* 下载文件相关结构体 */
 typedef struct
 {
@@ -135,6 +139,16 @@ typedef struct
     const char *status;      /* 状态: download_success/download_fail/delete_success/delete_fail */
 } LnEmojiGroup;
 
+/* Emoji 上报(队列透传)邮件结构体 - 固定大小,避免二次内存管理 */
+typedef struct
+{
+    char characterId[64];
+    char eyesColor[32];
+    char eyesShape[32];
+    char status[32];       /* download_success/download_fail/delete_success/delete_fail */
+    char description[64];  /* 描述,如 “下载成功” */
+} ln_emoji_report_mail_t;
+
 /* 设备绑定状态 */
 typedef enum
 {
@@ -198,6 +212,8 @@ const char *uapi_ln_ota_get_saved_path(void);
 const char *uapi_ln_get_audio_path(void);
 /* 获取设备绑定用户ID */
 const char *uapi_ln_get_user_id(void);
+//获取当前是否正在下载文件
+int uapi_ln_is_file_downloading(void);
 /*文件下载函数:从URL下载到指定路径*/
 int uapi_ln_download_file(const char *url, const char *save_path);
 int uapi_ln_download_with_retry(const char *url, const char *save_path, int max_retries);
@@ -228,6 +244,7 @@ uint32_t ln_token_load_json(char **token_out, uint64_t *expire_timestamp_out);
 uint32_t ln_token_is_expired(int *expired_out);
 /* 主动刷新设备Token(调用HTTP接口),并落盘保存 */
 uint32_t uapi_ln_refresh_device_token(void);
+void ln_start_emoji_download(void);
 
 /* 从本地读取角色配置 */
 void ln_read_character_config(void);
@@ -247,6 +264,8 @@ void ln_parse_schedule_reminder_config(const char *json_str, int json_len);
 void ln_parse_download_emoji(const char *json_str, int json_len);
 /* 解析删除表情 */
 void ln_parse_delete_emoji(const char *json_str, int json_len);
+/* 上报角色表情组下载状态(从队列数据解析)*/
+void ln_emoji_report_status(char *data);
 
 /* ============================== 各接口函数声明 ============================== */
 uint32_t ln_get_device_token_http(const char *device_id,
index 38e1360..ef687da 100644 (file)
@@ -409,7 +409,7 @@ void ln_parse_character_config_local(const char *json_buf, int json_len)
     if (access(LN_DEVICE_SCREEN_IMAGE_FILE, F_OK) != -1)
     {
         WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DEVICE_CHARACTER] 屏保图片存在,直接发送切换屏保通知");
-        push_screen_image_download_success_notification();
+        push_download_success_notification(DOWNLOAD_FILE_TYPE_SCREEN_IMAGE);
     }
     else
     {
@@ -439,6 +439,10 @@ void ln_parse_character_config_local(const char *json_buf, int json_len)
                     memcpy(req.save_path, save, slen + 1);
                 }
             }
+            
+            //通知ui切换slice下载屏保
+            push_download_file_notification(DOWNLOAD_FILE_TYPE_SCREEN_IMAGE);
+
             do_ln_http_server_send_data(LN_HTTP_SERVICE_CMD_DOWNLOAD_SCREEN_IMAGE, (char *)&req, sizeof(req));
         }
 
index 2022490..96c91e6 100644 (file)
 /* 表情信息结构体定义 */
 typedef struct
 {
-    char emotion[32];   // 心情类型
-    char emojiUrl[512]; // 表情文件URL
+    char *emotion;   // 心情类型(动态分配)
+    char *emojiUrl;  // 表情文件URL(动态分配)
 } EmotionEmoji;
 
 /* 表情包配置结构体 */
 typedef struct
 {
-    char characterId[64]; // 角色ID
-    char eyesColor[32];   // 瞳色
-    char eyesShape[32];   // 眼型
+    char *characterId; // 角色ID(动态分配)
+    char *eyesColor;   // 瞳色(动态分配)
+    char *eyesShape;   // 眼型(动态分配)
     EmotionEmoji *emojis; // 表情列表
     int emojiCount;       // 表情数量
+    long long totalSize;  // 服务端汇报的表情包总大小(字节)
 } EmojiPackage;
 
 /* 全局变量 */
 EmojiPackage g_emoji_package = {0};
-static volatile int g_emoji_downloading = 0; // 下载状态标志
 
 /* 安全字符串复制函数 */
 static inline void ln_safe_strcpy(char *dst, size_t dst_size, const char *src)
@@ -47,15 +47,58 @@ static inline void ln_safe_strcpy(char *dst, size_t dst_size, const char *src)
     dst[dst_size - 1] = '\0';
 }
 
-/* 释放表情列表资源 */
+/* 动态分配字符串(返回非NULL,至少为空串)*/
+static inline char *ln_safe_strdup(const char *src)
+{
+    const char *s = src ? src : "";
+    size_t n = strlen(s) + 1;
+    char *p = (char *)malloc(n);
+    if (!p)
+    {
+        return NULL;
+    }
+    memcpy(p, s, n);
+    return p;
+}
+
+/* 释放表情包所有动态资源 */
 static void ln_free_emoji_list()
 {
+    if (g_emoji_package.characterId)
+    {
+        free(g_emoji_package.characterId);
+        g_emoji_package.characterId = NULL;
+    }
+    if (g_emoji_package.eyesColor)
+    {
+        free(g_emoji_package.eyesColor);
+        g_emoji_package.eyesColor = NULL;
+    }
+    if (g_emoji_package.eyesShape)
+    {
+        free(g_emoji_package.eyesShape);
+        g_emoji_package.eyesShape = NULL;
+    }
     if (g_emoji_package.emojis)
     {
+        for (int i = 0; i < g_emoji_package.emojiCount; i++)
+        {
+            if (g_emoji_package.emojis[i].emotion)
+            {
+                free(g_emoji_package.emojis[i].emotion);
+                g_emoji_package.emojis[i].emotion = NULL;
+            }
+            if (g_emoji_package.emojis[i].emojiUrl)
+            {
+                free(g_emoji_package.emojis[i].emojiUrl);
+                g_emoji_package.emojis[i].emojiUrl = NULL;
+            }
+        }
         free(g_emoji_package.emojis);
         g_emoji_package.emojis = NULL;
     }
     g_emoji_package.emojiCount = 0;
+    g_emoji_package.totalSize = 0;
 }
 
 /* 工具函数:生成表情缓存路径 */
@@ -67,9 +110,15 @@ static void ln_gen_emoji_cache_path(const EmojiPackage *pkg, const EmotionEmoji
         return;
     }
 
-    // 路径格式:BASE/characterId/eyesColor_eyesShape/emotion.ext
+    const char *characterId = pkg->characterId ? pkg->characterId : "";
+    const char *eyesColor = pkg->eyesColor ? pkg->eyesColor : "";
+    const char *eyesShape = pkg->eyesShape ? pkg->eyesShape : "";
+    const char *emotion = emoji->emotion ? emoji->emotion : "";
+    const char *emojiUrl = emoji->emojiUrl ? emoji->emojiUrl : "";
+
+    // 路径格式:EMOJI_CACHE_BASE_PATH/eyesShape/eyesColor/emotion.mp4
     char ext[32] = {0};
-    const char *dot = strrchr(emoji->emojiUrl, '.');
+    const char *dot = emojiUrl[0] ? strrchr(emojiUrl, '.') : NULL;
     if (dot && strlen(dot) < sizeof(ext))
     {
         strncpy(ext, dot, sizeof(ext) - 1);
@@ -80,16 +129,15 @@ static void ln_gen_emoji_cache_path(const EmojiPackage *pkg, const EmotionEmoji
         return;
     }
 
-    // emojiUrl=https://ambybox-emoji.test.leiniao.com/v1/emoji/getEmoji?characterId=1&eyesColor=1&eyesShape=1&emotion=1
-    // path=/user/userdata/leiniao/emoji/1/1_1/1.png
+    //表情存放路径
+    // 生成表情缓存路径成功,path=/user/userdata/leiniao/emoji/round-eyes/blue/angry.mp4
 
     snprintf(path, path_len,
-             "%s%s/%s_%s/%s%s",
+             "%s%s/%s/%s%s",
              EMOJI_CACHE_BASE_PATH,
-             pkg->characterId,
-             pkg->eyesColor,
-             pkg->eyesShape,
-             emoji->emotion,
+             eyesShape,
+             eyesColor,
+             emotion,
              ext);
     WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD] 生成表情缓存路径成功,path=%s", path);
 }
@@ -110,11 +158,26 @@ static void ln_emoji_download(void *arg)
     if (!pkg || pkg->emojiCount <= 0 || !pkg->emojis)
     {
         WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD] 没有表情需要下载");
-        g_emoji_downloading = 0;
+        ln_free_emoji_list();
         return;
     }
 
     int all_success = 1;
+    int downloaded_count = 0; /* 包含已缓存与成功下载的 */
+    int failed_count = 0;
+
+    /* 若有总大小信息,则进行磁盘空间预检查(考虑已缓存文件大小) */
+    if (pkg->totalSize > 0)
+    {
+        if (can_download_file(EMOJI_CACHE_BASE_PATH, pkg->totalSize) == 0)
+        {
+            WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD] 没有足够的空间,跳过下载");
+            //通知ui
+            push_api_error_notification("没有足够的空间,跳过下载");
+            ln_free_emoji_list();
+            return;
+        }
+    }
 
     // 遍历下载所有表情
     for (int i = 0; i < pkg->emojiCount; i++)
@@ -122,7 +185,8 @@ static void ln_emoji_download(void *arg)
         // 跳过已缓存的文件
         if (ln_emoji_is_cached(pkg, &pkg->emojis[i]))
         {
-            WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD] emoji %s already cached", pkg->emojis[i].emotion);
+            WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD] emoji %s 已缓存", pkg->emojis[i].emotion);
+            downloaded_count++;
             continue;
         }
 
@@ -143,43 +207,57 @@ static void ln_emoji_download(void *arg)
                 uapi_create_folder(dir_buf);
             }
         }
-        if (uapi_ln_download_file(pkg->emojis[i].emojiUrl, save_path) != ERRCODE_SUCC)
+        if (uapi_ln_download_with_retry(pkg->emojis[i].emojiUrl, save_path, 3) != ERRCODE_SUCC)
         {
             all_success = 0;
-            WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD] emoji %s download failed", pkg->emojis[i].emotion);
+            failed_count++;
+            WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD] emoji %s 下载失败", pkg->emojis[i].emotion);
+        }
+        else
+        {
+            downloaded_count++;
+            WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD] emoji %s 下载完成", pkg->emojis[i].emotion);
         }
     }
 
+    /* 汇总统计打印 */
+    {
+        int remaining_count = pkg->emojiCount - downloaded_count;
+        if (remaining_count < 0)
+            remaining_count = 0;
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,
+                      "[DOWNLOAD] 统计: 总数=%d, 已完成=%d, 失败=%d, 剩余未下载=%d",
+                      pkg->emojiCount, downloaded_count, failed_count, remaining_count);
+    }
+
     WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "上报表情下载状态");
 
     // 下载完成后上报状态
-    const char *status = all_success ? "success" : "failed";
-    /* 传递包含'\0'的长度,确保下游按字符串安全处理 */
-    do_ln_http_server_send_data(LN_HTTP_SERVICE_CMD_EMOJI_REPORT, (char *)status, (uint32_t)(strlen(status) + 1));
-    WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD] report status %s, all_success: %d", status, all_success);
-
-    g_emoji_downloading = 0;
+    /* 构建上报邮件(包含状态与描述),遵循新协议 */
+    ln_emoji_report_mail_t mail = {0};
+    ln_safe_strcpy(mail.characterId, sizeof(mail.characterId), g_emoji_package.characterId);
+    ln_safe_strcpy(mail.eyesColor, sizeof(mail.eyesColor), g_emoji_package.eyesColor);
+    ln_safe_strcpy(mail.eyesShape, sizeof(mail.eyesShape), g_emoji_package.eyesShape);
+    ln_safe_strcpy(mail.status, sizeof(mail.status), all_success ? "download_success" : "download_fail");
+    ln_safe_strcpy(mail.description, sizeof(mail.description), all_success ? "下载成功" : "下载失败");
+    do_ln_http_server_send_data(LN_HTTP_SERVICE_CMD_EMOJI_REPORT, (char *)&mail, (uint32_t)sizeof(mail));
+    WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD] report status %s, all_success: %d", mail.status, all_success);
+
+    /* 下载完释放本次表情包占用的内存 */
+    ln_free_emoji_list();
 }
 
-/* 启动下载任务 */
-static void ln_start_emoji_download()
+/* 启动表情下载任务 */
+void ln_start_emoji_download(void)
 {
-    if (g_emoji_downloading)
-    {
-        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD] already downloading, skip");
-        return;
-    }
-
+    WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD] 启动表情下载任务");
     if (g_emoji_package.emojiCount <= 0 || !g_emoji_package.emojis)
     {
         WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD] no emojis to download");
         return;
     }
-
     // 下载表情
     ln_emoji_download(&g_emoji_package);
-
-    g_emoji_downloading = 1;
 }
 
 /* 解析下载表情命令JSON */
@@ -190,6 +268,12 @@ void ln_parse_download_emoji(const char *json_buf, int json_len)
         WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD_EMOJI] invalid json input");
         return;
     }
+    if(uapi_ln_is_file_downloading() == 1)
+    {
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD_EMOJI] 已有文件正在下载中,跳过下载");
+        // push_api_error_notification("已有文件正在下载中,跳过下载");
+        return;
+    }
 
     // 释放原有表情资源
     ln_free_emoji_list();
@@ -236,17 +320,29 @@ void ln_parse_download_emoji(const char *json_buf, int json_len)
     cJSON *eyesColor = cJSON_GetObjectItem(data, "eyesColor");
     cJSON *eyesShape = cJSON_GetObjectItem(data, "eyesShape");
 
-    ln_safe_strcpy(g_emoji_package.characterId,
-                   sizeof(g_emoji_package.characterId),
-                   (characterId && cJSON_IsString(characterId)) ? characterId->valuestring : "");
-
-    ln_safe_strcpy(g_emoji_package.eyesColor,
-                   sizeof(g_emoji_package.eyesColor),
-                   (eyesColor && cJSON_IsString(eyesColor)) ? eyesColor->valuestring : "");
+    g_emoji_package.characterId = ln_safe_strdup((characterId && cJSON_IsString(characterId)) ? characterId->valuestring : "");
+    g_emoji_package.eyesColor   = ln_safe_strdup((eyesColor && cJSON_IsString(eyesColor)) ? eyesColor->valuestring : "");
+    g_emoji_package.eyesShape   = ln_safe_strdup((eyesShape && cJSON_IsString(eyesShape)) ? eyesShape->valuestring : "");
 
-    ln_safe_strcpy(g_emoji_package.eyesShape,
-                   sizeof(g_emoji_package.eyesShape),
-                   (eyesShape && cJSON_IsString(eyesShape)) ? eyesShape->valuestring : "");
+    /* 解析总大小(字节) */
+    cJSON *size_node = cJSON_GetObjectItem(data, "size");
+    if (size_node && (cJSON_IsNumber(size_node) || cJSON_IsString(size_node)))
+    {
+        long long parsed_size = 0;
+        if (cJSON_IsNumber(size_node))
+        {
+            parsed_size = (long long)size_node->valuedouble;
+        }
+        else if (cJSON_IsString(size_node) && size_node->valuestring)
+        {
+            parsed_size = atoll(size_node->valuestring);
+        }
+        g_emoji_package.totalSize = parsed_size > 0 ? parsed_size : 0;
+    }
+    else
+    {
+        g_emoji_package.totalSize = 0;
+    }
 
     // 解析表情列表
     cJSON *emotionEmojis = cJSON_GetObjectItem(data, "emotionEmojis");
@@ -256,7 +352,7 @@ void ln_parse_download_emoji(const char *json_buf, int json_len)
         if (count > 0)
         {
             g_emoji_package.emojiCount = count;
-            g_emoji_package.emojis = (EmotionEmoji *)malloc(sizeof(EmotionEmoji) * count);
+            g_emoji_package.emojis = (EmotionEmoji *)calloc((size_t)count, sizeof(EmotionEmoji));
             if (!g_emoji_package.emojis)
             {
                 WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD_EMOJI] malloc failed for emojis");
@@ -273,13 +369,8 @@ void ln_parse_download_emoji(const char *json_buf, int json_len)
                         cJSON *emotion = cJSON_GetObjectItem(item, "emotion");
                         cJSON *emojiUrl = cJSON_GetObjectItem(item, "emojiUrl");
 
-                        ln_safe_strcpy(g_emoji_package.emojis[i].emotion,
-                                       sizeof(g_emoji_package.emojis[i].emotion),
-                                       (emotion && cJSON_IsString(emotion)) ? emotion->valuestring : "");
-
-                        ln_safe_strcpy(g_emoji_package.emojis[i].emojiUrl,
-                                       sizeof(g_emoji_package.emojis[i].emojiUrl),
-                                       (emojiUrl && cJSON_IsString(emojiUrl)) ? emojiUrl->valuestring : "");
+                        g_emoji_package.emojis[i].emotion = ln_safe_strdup((emotion && cJSON_IsString(emotion)) ? emotion->valuestring : "");
+                        g_emoji_package.emojis[i].emojiUrl = ln_safe_strdup((emojiUrl && cJSON_IsString(emojiUrl)) ? emojiUrl->valuestring : "");
                     }
                 }
             }
@@ -292,9 +383,8 @@ void ln_parse_download_emoji(const char *json_buf, int json_len)
     WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "  eyesColor: %s", g_emoji_package.eyesColor);
     WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "  eyesShape: %s", g_emoji_package.eyesShape);
     WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "  emojiCount: %d", g_emoji_package.emojiCount);
+    WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "  totalSize: %lld", g_emoji_package.totalSize);
 
-    // 通知UI切换表情
-    // push_download_emoji_notification(g_emoji_package.characterId, g_emoji_package.eyesColor, g_emoji_package.eyesShape);
 
     for (int i = 0; i < g_emoji_package.emojiCount; i++)
     {
@@ -304,9 +394,11 @@ void ln_parse_download_emoji(const char *json_buf, int json_len)
                       g_emoji_package.emojis[i].emojiUrl);
     }
 
-    // 解析完成后启动下载任务
-    ln_start_emoji_download();
-
+    // 解析完成后启动下载任务(通过发送队列触发下载逻辑)
+    // 说明:下载所需信息已填充到全局 g_emoji_package,触发命令无需携带负载
+    //通知ui切换slice下载表情
+    push_download_file_notification(DOWNLOAD_FILE_TYPE_EMOJI);
+    do_ln_http_server_send_data(LN_HTTP_SERVICE_CMD_DOWNLOAD_EMOJI, NULL, 0);
     // 清理资源
     cJSON_Delete(root);
     free(text);
@@ -427,11 +519,10 @@ void ln_parse_delete_emoji(const char *json_buf, int json_len)
     // 删除本地表情缓存
     char del_dir[MAX_PATH_LEN] = {0};
     snprintf(del_dir, sizeof(del_dir),
-             "%s%s/%s_%s",
+             "%s%s/%s",
              EMOJI_CACHE_BASE_PATH,
-             del_characterId,
-             del_eyesColor,
-             del_eyesShape);
+             del_eyesShape,
+             del_eyesColor);
 
     if (ln_rmdir_recursive(del_dir) == 0)
     {
@@ -447,13 +538,14 @@ void ln_parse_delete_emoji(const char *json_buf, int json_len)
     free(text);
 }
 
-/* 7.1 上报角色表情组下载状态 */
+/* 7.1 上报角色表情组下载状态(支持 description) */
 uint32_t ln_emoji_report_status_http(const char *device_id,
                                      const char *device_token,
                                      const char *character_id,
                                      const char *eyes_color,
                                      const char *eyes_shape,
-                                     const char *status)
+                                     const char *status,
+                                     const char *description)
 {
     if (!device_id || !device_token || !character_id || !eyes_color || !eyes_shape || !status)
     {
@@ -505,6 +597,7 @@ uint32_t ln_emoji_report_status_http(const char *device_id,
     cJSON_AddStringToObject(root, "eyesColor", eyes_color);
     cJSON_AddStringToObject(root, "eyesShape", eyes_shape);
     cJSON_AddStringToObject(root, "status", status);
+    cJSON_AddStringToObject(root, "description", description ? description : "");
     char *body = cJSON_PrintUnformatted(root);
     cJSON_Delete(root);
     if (!body)
@@ -544,6 +637,14 @@ uint32_t ln_emoji_report_status_http(const char *device_id,
     }
 
     int code = HttpClientGetResponseCode(&client);
+    if (code != 200)
+    {
+        free(body);
+        HttpClientUnprepare(&clientData);
+        free(header_buf);
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[EMOJI_REPORT_STATUS] response code not 200");
+        return ERRCODE_FAIL;
+    }
     if (clientData.responseBuf && clientData.responseBufLen > 0)
     {
         // {
@@ -564,14 +665,7 @@ uint32_t ln_emoji_report_status_http(const char *device_id,
         WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[EMOJI_REPORT_STATUS] response body: %s", response_buf);
         free(response_buf);
     }
-    if (code != 200)
-    {
-        free(body);
-        HttpClientUnprepare(&clientData);
-        free(header_buf);
-        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[EMOJI_REPORT_STATUS] response code not 200");
-        return ERRCODE_FAIL;
-    }
+
 
     free(body);
     HttpClientUnprepare(&clientData);
@@ -695,14 +789,30 @@ uint32_t ln_emoji_report_all_http(const char *device_id,
 }
 
 /* 上报角色表情组下载状态 */
-void ln_emoji_report_status(char* status)
+void ln_emoji_report_status(char* data)
 {
-    ln_emoji_report_status_http(uapi_ln_get_device_id(),
-                                uapi_ln_get_device_token(),
-                                g_emoji_package.characterId,
-                                g_emoji_package.eyesColor,
-                                g_emoji_package.eyesShape,
-                                status);
+    if (!data) {
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[EMOJI_REPORT_STATUS] empty mail data");
+        return;
+    }
+    ln_emoji_report_mail_t mail;
+    memcpy(&mail, data, sizeof(mail));
+
+    const char *device_id = uapi_ln_get_device_id();
+    const char *device_token = uapi_ln_get_device_token();
+
+    if (!device_id || !device_token) {
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[EMOJI_REPORT_STATUS] missing device id/token");
+        return;
+    }
+
+    ln_emoji_report_status_http(device_id,
+                                device_token,
+                                mail.characterId,
+                                mail.eyesColor,
+                                mail.eyesShape,
+                                mail.status,
+                                mail.description);
 }
 
 /* 上报所有角色表情组状态 */
index 946c434..5a24c2e 100644 (file)
@@ -118,6 +118,16 @@ uint64_t get_folder_size(const char *folder_path)
     
     return memoryManager->GetFolderSize(folder_path);
 }
+
+int can_download_file(const char *path, uint64_t downloadSize)
+{
+    OHOS::MemoryManager* memoryManager = OHOS::MemoryManager::GetInstance();
+    if (memoryManager == NULL) {
+        return 0;
+    }
+    int ret = memoryManager->CheckPathMemory(path, downloadSize);
+    return ret ? 1 : 0;
+}
 // 推送角色配置更改通知(简洁载荷)
 void push_character_config_change_notification(const char* characterId,
     const char *characterName,
@@ -215,11 +225,18 @@ void push_bind_and_unbind_notification(int is_bind)
     SendMsgToUIService(HAND_BIND_AND_UNBIND_NOTIFY, 0, &is_bind, sizeof(is_bind));
 }
 
-// 推送屏保下载成功通知到UI
-void push_screen_image_download_success_notification(void)
+// 通知ui需要下载文件了需要切换slice
+void push_download_file_notification(int type)
+{
+    WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD_FILE] type=%d", type);
+    SendMsgToUIService(HAND_DOWNLOAD_FILE_NOTIFY, 0, &type, sizeof(type));
+}
+
+// 推送文件下载成功通知到UI
+void push_download_success_notification(int type)
 {
-    WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[SCREEN_IMAGE_DOWNLOAD_SUCCESS]");
-    SendMsgToUIService(HAND_SCREEN_IMAGE_DOWNLOAD_SUCCESS_NOTIFY, 0, NULL, 0);
+    WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD_SUCCESS] type=%d", type);
+    SendMsgToUIService(HAND_DOWNLOAD_SUCCESS_NOTIFY, 0, &type, sizeof(type));
 }
 
 #ifdef __cplusplus
index 8bfe15e..2e422e0 100644 (file)
@@ -57,6 +57,8 @@ void push_task_reminder_notification(const char *scheduleName);
 // 内存管理相关C接口
 const char* get_system_memory_info(void);
 uint64_t get_folder_size(const char *folder_path);
+//获取能否下载文件
+int can_download_file(const char *path, uint64_t downloadSize);
 
 //推送角色(配置更改)/切换通知
 void push_character_config_change_notification(
@@ -84,9 +86,14 @@ bool switch_expression_video(const char *video_path);
 void push_api_error_notification(const char *message);
 //通知ui绑定和解绑
 void push_bind_and_unbind_notification(int is_bind);
-// 推送屏保下载成功通知到UI
-void push_screen_image_download_success_notification(void);
-
+enum {
+    DOWNLOAD_FILE_TYPE_SCREEN_IMAGE    = 0,//屏保下载
+    DOWNLOAD_FILE_TYPE_EMOJI    = 1,       //表情下载
+};
+// 通知ui需要下载文件了需要切换slice
+void push_download_file_notification(int type);
+// 推送文件下载成功通知到UI
+void push_download_success_notification(int type);
 #ifdef __cplusplus
 }
 #endif