From: luozheng <1732036298@qq.com> Date: Wed, 29 Oct 2025 08:46:35 +0000 (+0800) Subject: 表情下载添加预估时间功能 X-Git-Tag: V1.0.9_20251128_194606_LN~152^2~1^2~2 X-Git-Url: http://git.snicker.com.cn/?a=commitdiff_plain;h=7d804f8bba449df7f6af64470f26603748ce405f;p=mirrors%2FFD230.git 表情下载添加预估时间功能 --- diff --git a/code/sdk/application/wearable/ln/nativeapp/nativeui/include/uiservice/ui_service.h b/code/sdk/application/wearable/ln/nativeapp/nativeui/include/uiservice/ui_service.h index 8966aefac..4c3fb31ca 100644 --- a/code/sdk/application/wearable/ln/nativeapp/nativeui/include/uiservice/ui_service.h +++ b/code/sdk/application/wearable/ln/nativeapp/nativeui/include/uiservice/ui_service.h @@ -65,6 +65,7 @@ typedef enum { HAND_DOWNLOAD_SUCCESS_NOTIFY, // 下载成功通知 HAND_DOWNLOAD_FAIL_NOTIFY, // 下载失败通知 HAND_DOWNLOAD_PROGRESS_NOTIFY, // 下载进度通知 + HAND_DOWNLOAD_ETA_NOTIFY, // 下载ETA(剩余时间)通知,单位秒,-1表示未知 HAND_AUDIO_PLAYBACK_START_NOTIFY, // 音频播放开始通知 HAND_AUDIO_PLAYBACK_END_NOTIFY, // 音频播放结束通知 HAND_AUDIO_RECORDING_END_NOTIFY, // 录音结束通知 diff --git a/code/sdk/application/wearable/ln/nativeapp/nativeui/ota/include/OtaDownloadView.h b/code/sdk/application/wearable/ln/nativeapp/nativeui/ota/include/OtaDownloadView.h index cd1b4e04b..233a4891d 100644 --- a/code/sdk/application/wearable/ln/nativeapp/nativeui/ota/include/OtaDownloadView.h +++ b/code/sdk/application/wearable/ln/nativeapp/nativeui/ota/include/OtaDownloadView.h @@ -33,6 +33,9 @@ namespace OHOS // 更新下载状态文本 void UpdateDownloadStatus(const char* status); + + // 更新下载ETA + void UpdateDownloadEta(const char* eta); // 设置下载类型 void SetDownloadType(int type); diff --git a/code/sdk/application/wearable/ln/nativeapp/nativeui/ota/include/OtaModel.h b/code/sdk/application/wearable/ln/nativeapp/nativeui/ota/include/OtaModel.h index fe2e0c785..d555ffe1a 100644 --- a/code/sdk/application/wearable/ln/nativeapp/nativeui/ota/include/OtaModel.h +++ b/code/sdk/application/wearable/ln/nativeapp/nativeui/ota/include/OtaModel.h @@ -70,6 +70,7 @@ namespace OHOS void HandleDownloadSuccess(int type); void HandleDownloadProgress(int progress); + void HandleDownloadEta(int seconds); void HandleDownloadFail(int type); private: OtaModel(); diff --git a/code/sdk/application/wearable/ln/nativeapp/nativeui/ota/src/OtaDownloadView.cpp b/code/sdk/application/wearable/ln/nativeapp/nativeui/ota/src/OtaDownloadView.cpp index 2ab1584a5..1608b81d4 100644 --- a/code/sdk/application/wearable/ln/nativeapp/nativeui/ota/src/OtaDownloadView.cpp +++ b/code/sdk/application/wearable/ln/nativeapp/nativeui/ota/src/OtaDownloadView.cpp @@ -90,7 +90,7 @@ namespace OHOS RemindLabel->SetFont(DEFAULT_VECTOR_FONT_FILENAME, 18); RemindLabel->SetStyle(STYLE_TEXT_COLOR, 0xffffffff); - RemindLabel->SetText("预计需要18分钟"); + // RemindLabel->SetText("预计需要18分钟"); RemindLabel->SetVisible(true); group->Add(RemindLabel); } @@ -144,6 +144,14 @@ namespace OHOS } } + void OtaDownloadView::UpdateDownloadEta(const char* eta) + { + if (RemindLabel && eta) { + RemindLabel->SetText(eta); + RemindLabel->Invalidate(); + WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "OtaDownloadView::UpdateDownloadEta: %s", eta); + } + } void OtaDownloadView::SetDownloadType(int type) { downloadType = type; diff --git a/code/sdk/application/wearable/ln/nativeapp/nativeui/ota/src/OtaModel.cpp b/code/sdk/application/wearable/ln/nativeapp/nativeui/ota/src/OtaModel.cpp index 0fff6491c..8a6294285 100644 --- a/code/sdk/application/wearable/ln/nativeapp/nativeui/ota/src/OtaModel.cpp +++ b/code/sdk/application/wearable/ln/nativeapp/nativeui/ota/src/OtaModel.cpp @@ -427,6 +427,26 @@ namespace OHOS } } + // 下载ETA(单位秒,-1表示未知):用于在下载界面展示预估剩余时间 + void OtaModel::HandleDownloadEta(int seconds) + { + if(OtaDownloadView::GetInstance()) + { + char buf[64] = {0}; + if (seconds >= 0) + { + //只显示分钟,不显示秒 + int mm = seconds / 60; + (void)snprintf(buf, sizeof(buf) - 1, "预计需要%d分钟", mm); + } + else + { + (void)snprintf(buf, sizeof(buf) - 1, "预计需要 —"); + } + OtaDownloadView::GetInstance()->UpdateDownloadEta(buf); + } + } + //下载失败通知 void OtaModel::HandleDownloadFail(int type) { diff --git a/code/sdk/application/wearable/ln/nativeapp/nativeui/uiservice/src/ui_service.cpp b/code/sdk/application/wearable/ln/nativeapp/nativeui/uiservice/src/ui_service.cpp index 4ceb644fd..201e41214 100644 --- a/code/sdk/application/wearable/ln/nativeapp/nativeui/uiservice/src/ui_service.cpp +++ b/code/sdk/application/wearable/ln/nativeapp/nativeui/uiservice/src/ui_service.cpp @@ -101,6 +101,7 @@ static void HandDownloadFileNotify(Request *request); static void HandDownloadSuccessNotify(Request *request); static void HandDownloadFailNotify(Request *request); static void HandDownloadProgressNotify(Request *request); +static void HandDownloadEtaNotify(Request *request); static void HandAudioPlaybackStartNotify(Request *request); static void HandAudioPlaybackEndNotify(Request *request); static void HandAudioRecordingEndNotify(Request *request); @@ -123,6 +124,7 @@ MsgHandlerEntry msgHandlers[] = { {HAND_DOWNLOAD_SUCCESS_NOTIFY,HandDownloadSuccessNotify}, {HAND_DOWNLOAD_FAIL_NOTIFY,HandDownloadFailNotify}, {HAND_DOWNLOAD_PROGRESS_NOTIFY,HandDownloadProgressNotify}, + {HAND_DOWNLOAD_ETA_NOTIFY,HandDownloadEtaNotify}, {HAND_AUDIO_PLAYBACK_START_NOTIFY,HandAudioPlaybackStartNotify}, {HAND_AUDIO_PLAYBACK_END_NOTIFY,HandAudioPlaybackEndNotify}, {HAND_AUDIO_RECORDING_END_NOTIFY,HandAudioRecordingEndNotify}, @@ -636,6 +638,16 @@ void HandDownloadProgressNotify(Request *request) std::bind(&OHOS::OtaModel::HandleDownloadProgress,OHOS::OtaModel::GetInstance(), progress)); } +// 下载ETA通知(单位秒,-1表示未知) +void HandDownloadEtaNotify(Request *request) +{ + WEARABLE_LOGE(WEARABLE_LOG_MODULE_UI_SERVICE, "HandDownloadEtaNotify msg_id = %d", request->msgId); + int seconds = *(int *)request->data; + WEARABLE_LOGE(WEARABLE_LOG_MODULE_UI_SERVICE, "HandDownloadEtaNotify seconds = %d", seconds); + GraphicService::GetInstance()->PostGraphicEvent( + std::bind(&OHOS::OtaModel::HandleDownloadEta,OHOS::OtaModel::GetInstance(), seconds)); +} + // 下载失败通知 void HandDownloadFailNotify(Request *request) { diff --git a/code/sdk/zhihui/leiniao/imsl_ln/ln_api_emoji.c b/code/sdk/zhihui/leiniao/imsl_ln/ln_api_emoji.c index 08972b48b..b2f414699 100644 --- a/code/sdk/zhihui/leiniao/imsl_ln/ln_api_emoji.c +++ b/code/sdk/zhihui/leiniao/imsl_ln/ln_api_emoji.c @@ -9,6 +9,7 @@ #include #include #include "custom_utils.h" +#include /* 宏定义 */ #define MAX_PATH_LEN 512 // 最大路径长度 @@ -34,6 +35,62 @@ typedef struct /* 全局变量 */ EmojiPackage g_emoji_package = {0}; +/* ========== ETA 粗略版辅助函数 ========== */ +/* 前置声明,便于在本节工具函数中调用 */ +static int ln_emoji_is_cached(const EmojiPackage *pkg, const EmotionEmoji *emoji); +static void ln_gen_emoji_cache_path(const EmojiPackage *pkg, const EmotionEmoji *emoji, char *path, size_t path_len); +/* 毫秒时间戳 */ +static inline unsigned long long ln_now_ms(void) +{ + struct timeval tv; + gettimeofday(&tv, NULL); + return (unsigned long long)tv.tv_sec * 1000ULL + (unsigned long long)tv.tv_usec / 1000ULL; +} + +/* 文件大小(不存在返回0)*/ +static unsigned long long ln_get_file_size(const char *path) +{ + if (!path) + return 0; + struct stat st; + if (stat(path, &st) == 0 && S_ISREG(st.st_mode) && st.st_size > 0) + return (unsigned long long)st.st_size; + return 0; +} + +/* 计算表情包总字节:优先用服务端 totalSize,否则汇总每个emoji的 size */ +static unsigned long long ln_calc_total_bytes_from_pkg(const EmojiPackage *pkg) +{ + if (!pkg) + return 0; + if (pkg->totalSize > 0) + return (unsigned long long)pkg->totalSize; + unsigned long long sum = 0; + for (int i = 0; i < pkg->emojiCount; ++i) + { + if (pkg->emojis[i].size > 0) + sum += (unsigned long long)pkg->emojis[i].size; + } + return sum; +} + +/* 计算已缓存完整文件的总字节 */ +static unsigned long long ln_calc_cached_full_bytes(const EmojiPackage *pkg) +{ + if (!pkg) + return 0; + unsigned long long sum = 0; + for (int i = 0; i < pkg->emojiCount; ++i) + { + if (ln_emoji_is_cached(pkg, &pkg->emojis[i])) + { + char path[MAX_PATH_LEN] = {0}; + ln_gen_emoji_cache_path(pkg, &pkg->emojis[i], path, sizeof(path)); + sum += ln_get_file_size(path); + } + } + return sum; +} /* 安全字符串复制函数 */ static inline void ln_safe_strcpy(char *dst, size_t dst_size, const char *src) @@ -379,6 +436,16 @@ static void ln_emoji_download(void *arg) int downloaded_count = 0; /* 包含已缓存与成功下载的 */ int failed_count = 0; + /* 粗略版 ETA 统计(本次会话)*/ + unsigned long long batch_start_ms = ln_now_ms(); + unsigned long long total_bytes_all = ln_calc_total_bytes_from_pkg(pkg); + unsigned long long cached_full_bytes = 0; + unsigned long long session_downloaded_bytes = 0; /* 仅统计本次会话新下载的字节 */ + /* 速率平滑(EMA)*/ + double ema_bps = 0.0; + int ema_ready = 0; + const double ETA_WARMUP_SECONDS = 5.0; /* 预热期内不推ETA或推未知,避免初期抖动 */ + /* 若有总大小信息,则进行磁盘空间预检查(考虑已缓存文件大小) */ if (pkg->totalSize > 0) { @@ -399,6 +466,13 @@ static void ln_emoji_download(void *arg) if (ln_emoji_is_cached(pkg, &pkg->emojis[ci])) { cached_count++; + /* 统计完整缓存字节,用于 ETA 剩余估算 */ + if (total_bytes_all > 0) + { + char path_bytes[MAX_PATH_LEN] = {0}; + ln_gen_emoji_cache_path(pkg, &pkg->emojis[ci], path_bytes, sizeof(path_bytes)); + cached_full_bytes += ln_get_file_size(path_bytes); + } } } downloaded_count = cached_count; @@ -425,6 +499,8 @@ static void ln_emoji_download(void *arg) // 下载并缓存(支持断点续传:当目标文件存在且小于应有大小时,从断点续传) char save_path[MAX_PATH_LEN] = {0}; ln_gen_emoji_cache_path(pkg, &pkg->emojis[i], save_path, sizeof(save_path)); + unsigned long long pre_size = ln_get_file_size(save_path); /* 记录下载前已有的部分大小(可能为0)*/ + unsigned long long file_start_ms = ln_now_ms(); /* 确保保存目录存在 */ { char dir_buf[MAX_PATH_LEN] = {0}; @@ -452,6 +528,83 @@ static void ln_emoji_download(void *arg) 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); + + /* 粗略版 ETA(平滑版):在单个文件完成后估算一次 */ + if (total_bytes_all > 0) + { + unsigned long long post_size = ln_get_file_size(save_path); + unsigned long long inc = 0; + if (post_size > pre_size) + inc = post_size - pre_size; + else if ((unsigned long long)pkg->emojis[i].size > 0) + inc = (unsigned long long)pkg->emojis[i].size; + else + inc = post_size; /* 兜底 */ + + session_downloaded_bytes += inc; + + unsigned long long now_ms = ln_now_ms(); + double elapsed_s = (now_ms > batch_start_ms) ? ((double)(now_ms - batch_start_ms) / 1000.0) : 0.0; + double avg_bps = (elapsed_s > 0.0) ? ((double)session_downloaded_bytes / elapsed_s) : 0.0; + + /* 单文件瞬时速率,用于更新EMA */ + double file_elapsed_s = (now_ms > file_start_ms) ? ((double)(now_ms - file_start_ms) / 1000.0) : 0.0; + if (file_elapsed_s > 0.0 && inc > 0) + { + double inst_bps = (double)inc / file_elapsed_s; + if (!ema_ready) + { + ema_bps = inst_bps; + ema_ready = 1; + } + else + { + const double alpha = 0.3; /* 平滑系数:越大越敏感 */ + ema_bps = alpha * inst_bps + (1.0 - alpha) * ema_bps; + } + } + + /* 选择用于ETA的速率:优先EMA,其次全局平均 */ + double used_bps = ema_ready ? ema_bps : avg_bps; + /* 轻度夹紧,避免异常跳变(以全局平均为中心)*/ + if (avg_bps > 0.0 && used_bps > 0.0) + { + double lower = avg_bps * 0.5; + double upper = avg_bps * 2.0; + if (used_bps < lower) used_bps = lower; + if (used_bps > upper) used_bps = upper; + } + + unsigned long long consumed = cached_full_bytes + session_downloaded_bytes; + unsigned long long remain_bytes = (total_bytes_all > consumed) ? (total_bytes_all - consumed) : 0ULL; + int eta_sec = -1; + /* 预热期内(或样本太少)不上报具体ETA,之后用平滑速率估算 */ + if (elapsed_s >= ETA_WARMUP_SECONDS || downloaded_count > 1) + { + if (used_bps > 1.0) + eta_sec = (int)(remain_bytes / used_bps); + } + + if (eta_sec >= 0) + { + WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, + "[DOWNLOAD][ETA] progress=%d%%, remain_bytes=%llu, avg=%.1f KB/s, eta=%ds", + progress, + (unsigned long long)remain_bytes, + used_bps / 1024.0, + eta_sec); + push_download_eta_notification(eta_sec); + } + else + { + WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, + "[DOWNLOAD][ETA] progress=%d%%, remain_bytes=%llu, avg=%.1f KB/s, eta=未知", + progress, + (unsigned long long)remain_bytes, + (avg_bps > 0.0 ? avg_bps : used_bps) / 1024.0); + push_download_eta_notification(-1); + } + } } } diff --git a/code/sdk/zhihui/leiniao/ln_api_cpp/ln_api_interface.cpp b/code/sdk/zhihui/leiniao/ln_api_cpp/ln_api_interface.cpp index d9a104e1c..cf8e931eb 100644 --- a/code/sdk/zhihui/leiniao/ln_api_cpp/ln_api_interface.cpp +++ b/code/sdk/zhihui/leiniao/ln_api_cpp/ln_api_interface.cpp @@ -254,6 +254,13 @@ void push_download_progress_notification(int progress) SendMsgToUIService(HAND_DOWNLOAD_PROGRESS_NOTIFY, 0, &progress, sizeof(progress)); } +// 推送文件下载ETA(秒)到UI(-1 表示未知) +void push_download_eta_notification(int seconds) +{ + WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD_ETA] seconds=%d", seconds); + SendMsgToUIService(HAND_DOWNLOAD_ETA_NOTIFY, 0, &seconds, sizeof(seconds)); +} + //通知音频播放开始了 void push_audio_playback_start_notification(void) { diff --git a/code/sdk/zhihui/leiniao/ln_api_cpp/ln_api_interface.h b/code/sdk/zhihui/leiniao/ln_api_cpp/ln_api_interface.h index ea27b270c..734d4a9bf 100644 --- a/code/sdk/zhihui/leiniao/ln_api_cpp/ln_api_interface.h +++ b/code/sdk/zhihui/leiniao/ln_api_cpp/ln_api_interface.h @@ -98,6 +98,8 @@ void push_download_success_notification(int type); void push_download_fail_notification(int type); // 推送文件下载进度通知到UI void push_download_progress_notification(int progress); +// 推送文件下载ETA(秒)到UI(-1 表示未知) +void push_download_eta_notification(int seconds); //通知音频播放结束了 void push_audio_playback_start_notification(void); void push_audio_playback_end_notification(void);