1.添加表情下载进度通知函数
authorluozheng <1732036298@qq.com>
Thu, 9 Oct 2025 02:59:59 +0000 (10:59 +0800)
committerluozheng <1732036298@qq.com>
Thu, 9 Oct 2025 02:59:59 +0000 (10:59 +0800)
2.添加表情组上报函数,待验证

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_emoji.c
code/sdk/zhihui/leiniao/imsl_ln/ln_api_story_random.c
code/sdk/zhihui/leiniao/ln_api_cpp/ln_api_interface.cpp
code/sdk/zhihui/leiniao/ln_api_cpp/ln_api_interface.h

index cb7eb50..4bd76c6 100644 (file)
@@ -1249,7 +1249,7 @@ static void ln_http_server_send_task(void const *arg)
         else if (mail.cmd == LN_HTTP_SERVICE_CMD_EMOJI_REPORT_ALL)
         {
             printf("LN_HTTP_SERVICE_CMD_EMOJI_REPORT_ALL:上报所有角色表情组状态\n");
-            // ln_emoji_report_all(mail.data);
+            ln_emoji_report_all();
         }
         else if (mail.cmd == LN_HTTP_SERVICE_CMD_DOWNLOAD_SCREEN_IMAGE)
         {
index 4511c05..7a761b0 100644 (file)
@@ -67,6 +67,7 @@ typedef struct
 #define LN_DEVICE_ICCID_FILE LN_DATA_PATH "/device_iccid.bin"
 #define LN_DEVICE_SCREEN_IMAGE_FILE LN_DATA_PATH "/device_screen_image.bin" // 屏保图片文件
 #define LN_REMINDER_AUDIO_FILE LN_DATA_PATH "/reminder_audio.mp3" // 日程提醒音频文件
+#define LN_EMOJI_GROUPS_FILE LN_DATA_PATH "/emoji/emoji_groups.json" /* 表情组持久化文件 */
 
 #define LN_CLIENT_PREPARE_SIZE 2 * 1024
 #define TEST_MP3_DOWNLOAD 0 // 测试下载mp3
index 90bb7a0..f8131ce 100644 (file)
@@ -102,6 +102,217 @@ static void ln_free_emoji_list()
     g_emoji_package.totalSize = 0;
 }
 
+/* 读取整个文件为字符串(成功返回0,失败返回-1;调用者负责free(*out_str))*/
+static int ln_read_file_to_string(const char *file_path, char **out_str)
+{
+    if (out_str)
+        *out_str = NULL;
+    if (!file_path || !out_str)
+        return -1;
+    FILE *fp = fopen(file_path, "rb");
+    if (!fp)
+        return -1;
+    if (fseek(fp, 0, SEEK_END) != 0)
+    {
+        fclose(fp);
+        return -1;
+    }
+    long len = ftell(fp);
+    if (len < 0 || len > (1024 * 1024))
+    {
+        fclose(fp);
+        return -1;
+    }
+    if (fseek(fp, 0, SEEK_SET) != 0)
+    {
+        fclose(fp);
+        return -1;
+    }
+    char *buf = (char *)malloc((size_t)len + 1);
+    if (!buf)
+    {
+        fclose(fp);
+        return -1;
+    }
+    size_t n = fread(buf, 1, (size_t)len, fp);
+    fclose(fp);
+    buf[n] = '\0';
+    *out_str = buf;
+    return 0;
+}
+
+/* 将字符串写入文件(成功返回0,失败返回-1)*/
+static int ln_write_string_to_file(const char *file_path, const char *str)
+{
+    if (!file_path || !str)
+        return -1;
+    /* 确保目录存在 */
+    uapi_create_folder((char *)LN_DATA_PATH);
+    uapi_create_folder((char *)(LN_DATA_PATH "/emoji"));
+    FILE *fp = fopen(file_path, "wb");
+    if (!fp)
+        return -1;
+    size_t len = strlen(str);
+    size_t n = fwrite(str, 1, len, fp);
+    fclose(fp);
+    return (n == len) ? 0 : -1;
+}
+
+/* 加载表情组根对象(包含emojiGroups数组);不存在则创建默认结构 */
+static cJSON *ln_load_emoji_groups_root(void)
+{
+    char *content = NULL;
+    if (ln_read_file_to_string(LN_EMOJI_GROUPS_FILE, &content) == 0 && content)
+    {
+        cJSON *root = cJSON_Parse(content);
+        free(content);
+        if (root)
+            return root;
+    }
+    /* 文件不存在或解析失败,创建默认 */
+    cJSON *root = cJSON_CreateObject();
+    if (!root)
+        return NULL;
+    cJSON *arr = cJSON_CreateArray();
+    if (!arr)
+    {
+        cJSON_Delete(root);
+        return NULL;
+    }
+    cJSON_AddItemToObject(root, "emojiGroups", arr);
+    return root;
+}
+
+/* 保存根对象到磁盘 */
+static int ln_save_emoji_groups_root(cJSON *root)
+{
+    if (!root)
+        return -1;
+    char *json = cJSON_PrintUnformatted(root);
+    if (!json)
+        return -1;
+    int ret = ln_write_string_to_file(LN_EMOJI_GROUPS_FILE, json);
+    cJSON_free(json);
+    return ret;
+}
+
+/* 获取或创建数组 */
+static cJSON *ln_get_or_create_groups_array(cJSON *root)
+{
+    if (!root)
+        return NULL;
+    cJSON *arr = cJSON_GetObjectItem(root, "emojiGroups");
+    if (!arr || !cJSON_IsArray(arr))
+    {
+        arr = cJSON_CreateArray();
+        if (!arr)
+            return NULL;
+        cJSON_ReplaceItemInObject(root, "emojiGroups", arr);
+    }
+    return arr;
+}
+
+/* 判断是否已存在条目 */
+static int ln_groups_contains(cJSON *arr, const char *characterId, const char *eyesColor, const char *eyesShape)
+{
+    if (!arr || !cJSON_IsArray(arr))
+        return 0;
+    int count = cJSON_GetArraySize(arr);
+    for (int i = 0; i < count; ++i)
+    {
+        cJSON *item = cJSON_GetArrayItem(arr, i);
+        if (!item || !cJSON_IsObject(item))
+            continue;
+        const char *cid = NULL, *color = NULL, *shape = NULL;
+        cJSON *v = cJSON_GetObjectItem(item, "characterId");
+        if (v && cJSON_IsString(v) && v->valuestring)
+            cid = v->valuestring;
+        v = cJSON_GetObjectItem(item, "eyesColor");
+        if (v && cJSON_IsString(v) && v->valuestring)
+            color = v->valuestring;
+        v = cJSON_GetObjectItem(item, "eyesShape");
+        if (v && cJSON_IsString(v) && v->valuestring)
+            shape = v->valuestring;
+        if (cid && color && shape &&
+            strcmp(cid, characterId ? characterId : "") == 0 &&
+            strcmp(color, eyesColor ? eyesColor : "") == 0 &&
+            strcmp(shape, eyesShape ? eyesShape : "") == 0)
+        {
+            return 1;
+        }
+    }
+    return 0;
+}
+
+/* 添加表情组到持久化 */
+static void ln_persist_add_emoji_group(const char *characterId, const char *eyesColor, const char *eyesShape)
+{
+    cJSON *root = ln_load_emoji_groups_root();
+    if (!root)
+        return;
+    cJSON *arr = ln_get_or_create_groups_array(root);
+    if (!arr)
+    {
+        cJSON_Delete(root);
+        return;
+    }
+    if (!ln_groups_contains(arr, characterId, eyesColor, eyesShape))
+    {
+        cJSON *item = cJSON_CreateObject();
+        if (item)
+        {
+            cJSON_AddStringToObject(item, "characterId", characterId ? characterId : "");
+            cJSON_AddStringToObject(item, "eyesColor", eyesColor ? eyesColor : "");
+            cJSON_AddStringToObject(item, "eyesShape", eyesShape ? eyesShape : "");
+            cJSON_AddItemToArray(arr, item);
+            (void)ln_save_emoji_groups_root(root);
+        }
+    }
+    cJSON_Delete(root);
+}
+
+/* 从持久化中移除表情组 */
+static void ln_persist_remove_emoji_group(const char *characterId, const char *eyesColor, const char *eyesShape)
+{
+    cJSON *root = ln_load_emoji_groups_root();
+    if (!root)
+        return;
+    cJSON *arr = ln_get_or_create_groups_array(root);
+    if (!arr)
+    {
+        cJSON_Delete(root);
+        return;
+    }
+    int count = cJSON_GetArraySize(arr);
+    for (int i = 0; i < count; ++i)
+    {
+        cJSON *item = cJSON_GetArrayItem(arr, i);
+        if (!item || !cJSON_IsObject(item))
+            continue;
+        const char *cid = NULL, *color = NULL, *shape = NULL;
+        cJSON *v = cJSON_GetObjectItem(item, "characterId");
+        if (v && cJSON_IsString(v) && v->valuestring)
+            cid = v->valuestring;
+        v = cJSON_GetObjectItem(item, "eyesColor");
+        if (v && cJSON_IsString(v) && v->valuestring)
+            color = v->valuestring;
+        v = cJSON_GetObjectItem(item, "eyesShape");
+        if (v && cJSON_IsString(v) && v->valuestring)
+            shape = v->valuestring;
+        if (cid && color && shape &&
+            strcmp(cid, characterId ? characterId : "") == 0 &&
+            strcmp(color, eyesColor ? eyesColor : "") == 0 &&
+            strcmp(shape, eyesShape ? eyesShape : "") == 0)
+        {
+            /* 删除该元素 */
+            cJSON_DeleteItemFromArray(arr, i);
+            (void)ln_save_emoji_groups_root(root);
+            break;
+        }
+    }
+    cJSON_Delete(root);
+}
+
 /* 工具函数:生成表情缓存路径 */
 static void ln_gen_emoji_cache_path(const EmojiPackage *pkg, const EmotionEmoji *emoji, char *path, size_t path_len)
 {
@@ -217,6 +428,9 @@ static void ln_emoji_download(void *arg)
         else
         {
             downloaded_count++;
+            /* 表情进度上报,按下载成功表情数量计算 */
+            int progress = (int)(((double)downloaded_count / pkg->emojiCount) * 100);
+            push_download_progress_notification(progress);
             WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD] emoji %s 下载完成", pkg->emojis[i].emotion);
         }
     }
@@ -246,6 +460,14 @@ static void ln_emoji_download(void *arg)
 
     /* 下载完释放本次表情包占用的内存 */
     ln_free_emoji_list();
+
+    /* 持久化:仅在整个表情组下载成功时记录该表情组 */
+    if (all_success)
+    {
+        /* 通知ui下载成功 */
+        push_download_success_notification(DOWNLOAD_FILE_TYPE_EMOJI);
+        ln_persist_add_emoji_group(mail.characterId, mail.eyesColor, mail.eyesShape);
+    }
 }
 
 /* 启动表情下载任务 */
@@ -555,6 +777,12 @@ void ln_parse_delete_emoji(const char *json_buf, int json_len)
     // 清理资源
     cJSON_Delete(root);
     free(text);
+
+    /* 持久化:删除成功则从持久化列表中移除该表情组 */
+    if (ret == 0)
+    {
+        ln_persist_remove_emoji_group(del_characterId, del_eyesColor, del_eyesShape);
+    }
 }
 
 /* 7.1 上报角色表情组下载状态(支持 description) */
@@ -758,7 +986,7 @@ static uint32_t ln_emoji_report_all_http(const char *device_id,
     if (!body)
         return ERRCODE_FAIL;
 
-    HTTPC_RESULT prep = ln_http_prepare_auto(&clientData, 2048, 8 * 1024);
+    HTTPC_RESULT prep = ln_http_prepare_auto(&clientData, 2048, LN_CLIENT_PREPARE_SIZE * 2);
     if (prep != HTTP_SUCCESS)
     {
         free(body);
@@ -768,6 +996,9 @@ static uint32_t ln_emoji_report_all_http(const char *device_id,
     clientData.postBuf = body;
     clientData.postBufLen = (int)strlen(body);
     clientData.postContentType = (char *)"application/json";
+    
+    //打印请求体
+    WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[EMOJI_REPORT_ALL] request body: %s", body);
 
     HTTPC_RESULT res = HttpClientPostRequest(&client, LN_EMOJI_REPORT_ALL_URL, &clientData);
     if (res != HTTP_SUCCESS)
@@ -779,6 +1010,14 @@ static uint32_t ln_emoji_report_all_http(const char *device_id,
     }
 
     int code = HttpClientGetResponseCode(&client);
+    if (code != 200)
+    {
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[EMOJI_REPORT_ALL] HTTP %d", code);
+        free(body);
+        HttpClientUnprepare(&clientData);
+        free(header_buf);
+        return ERRCODE_FAIL;
+    }
     if (clientData.responseBuf && clientData.responseBufLen > 0)
     {
         int real_len = clientData.responseContentLen > 0 ? clientData.responseContentLen : clientData.responseBufLen;
@@ -793,13 +1032,6 @@ static uint32_t ln_emoji_report_all_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);
-        return ERRCODE_FAIL;
-    }
 
     free(body);
     HttpClientUnprepare(&clientData);
@@ -837,8 +1069,51 @@ void ln_emoji_report_status(char* data)
 /* 上报所有角色表情组状态 */
 void ln_emoji_report_all(void)
 {
-    ln_emoji_report_all_http(uapi_ln_get_device_id(),
-                             uapi_ln_get_device_token(),
-                             g_emoji_package.emojis,
-                             g_emoji_package.emojiCount);
+    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_ALL] missing device id/token");
+        return;
+    }
+
+    cJSON *root = ln_load_emoji_groups_root();
+    if (!root)
+    {
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[EMOJI_REPORT_ALL] load root failed");
+        return;
+    }
+    cJSON *arr = ln_get_or_create_groups_array(root);
+    int count = (arr && cJSON_IsArray(arr)) ? cJSON_GetArraySize(arr) : 0;
+    if (count < 0)
+        count = 0;
+
+    LnEmojiGroup *groups = NULL;
+    if (count > 0)
+    {
+        groups = (LnEmojiGroup *)calloc((size_t)count, sizeof(LnEmojiGroup));
+        if (!groups)
+        {
+            cJSON_Delete(root);
+            return;
+        }
+        for (int i = 0; i < count; ++i)
+        {
+            cJSON *item = cJSON_GetArrayItem(arr, i);
+            cJSON *v = NULL;
+            v = cJSON_GetObjectItem(item, "characterId");
+            groups[i].characterId = (v && cJSON_IsString(v)) ? v->valuestring : "";
+            v = cJSON_GetObjectItem(item, "eyesColor");
+            groups[i].eyesColor = (v && cJSON_IsString(v)) ? v->valuestring : "";
+            v = cJSON_GetObjectItem(item, "eyesShape");
+            groups[i].eyesShape = (v && cJSON_IsString(v)) ? v->valuestring : "";
+            groups[i].status = "download_success"; /* 当前设备上已安装的表情组 */
+        }
+    }
+
+    (void)ln_emoji_report_all_http(device_id, device_token, groups, count);
+
+    if (groups)
+        free(groups);
+    cJSON_Delete(root);
 }
\ No newline at end of file
index e7cf97c..a6e289e 100644 (file)
@@ -269,7 +269,7 @@ void ln_parse_voice_chat_response(const char *json_str, int json_len)
         {
             WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "开始哄睡\n");
             push_sleep_mode_start_notification(NULL, 1); // 1意图识别结果
-            ln_ws_send_random_story_request(NULL);
+            ln_ws_send_random_story_request("随机故事");
         }
         else if(strcmp(intention_str, "EndSleepMode") == 0)
         {
index a656a7c..3b5d5a5 100644 (file)
@@ -240,6 +240,11 @@ void push_download_success_notification(int type)
     SendMsgToUIService(HAND_DOWNLOAD_SUCCESS_NOTIFY, 0, &type, sizeof(type));
 }
 
+// 推送文件下载进度通知到UI
+void push_download_progress_notification(int progress)
+{
+}
+
 #ifdef __cplusplus
 };
 #endif
\ No newline at end of file
index 1f9fb39..a656ed7 100644 (file)
@@ -94,6 +94,8 @@ enum {
 void push_download_file_notification(int type);
 // 推送文件下载成功通知到UI
 void push_download_success_notification(int type);
+// 推送文件下载进度通知到UI
+void push_download_progress_notification(int progress);
 #ifdef __cplusplus
 }
 #endif