return ERRCODE_SUCC;
}
+uint32_t ln_audio_player_pause(void)
+{
+ if (g_ln_audio_player == nullptr) {
+ return ERRCODE_SUCC;
+ }
+ (void)AudioStreamPlayerPause(g_ln_audio_player);
+ AudioStreamPlayerFlush(g_ln_audio_player);
+ return ERRCODE_SUCC;
+}
+
+uint32_t ln_audio_player_resume(void)
+{
+ if (g_ln_audio_player == nullptr) {
+ return ERRCODE_SUCC;
+ }
+ (void)AudioStreamPlayerResume(g_ln_audio_player);
+ return ERRCODE_SUCC;
+}
+
int32_t ln_audio_player_write(const uint8_t* data, uint32_t size)
{
if (g_ln_audio_player == nullptr) {
uint32_t ln_audio_player_init(void);
int32_t ln_audio_player_write(const uint8_t* data, uint32_t size);
int32_t ln_audio_player_get_stats(AudioStreamStats* stats);
+uint32_t ln_audio_player_pause(void);
+uint32_t ln_audio_player_resume(void);
#ifdef __cplusplus
}
char g_ln_last_target_version[64] = {0}; // 最近一次查询到的 OTA 包目标版本
char g_ln_last_package_md5[65] = {0}; // 最近一次查询到的 OTA 包 MD5
int g_ln_last_package_size = 0; // 最近一次查询到的 OTA 包大小(字节)
+static int g_ln_audio_download_cancel = 0; /* 0=正常, 1=取消 */
+int g_ln_is_device_bound = -1; /* 设备绑定状态 */
+char g_ln_device_user_id[64] = {0}; // 设备绑定用户ID
/* 获取设备id: 设备唯一标识 */
char *uapi_ln_get_device_id(void)
{
return "/user/leiniao/story_choose_audio.mp3";
}
+
+void uapi_ln_audio_download_is_cancel(int status)
+{
+ g_ln_audio_download_cancel = status;
+ if(g_ln_audio_download_cancel)
+ {
+ ln_audio_player_pause();
+ }
+ else
+ {
+ ln_audio_player_resume();
+ }
+}
+
+/* 获取是否绑定用户 */
+uint32_t uapi_ln_is_device_bound(void)
+{
+ return g_ln_is_device_bound;
+}
+
+/* 获取设备绑定用户ID */
+const char* uapi_ln_get_user_id(void)
+{
+ return g_ln_device_user_id;
+}
+
/* 自适应申请HttpClient缓冲(保证header>=2048),失败则逐步扩容重试 */
HTTPC_RESULT ln_http_prepare_auto(HttpClientData *clientData, int header_size, int resp_size)
{
return ERRCODE_FAIL;
}
- // 接收响应数据
+ // 接收响应数据(支持取消)
do
{
+ if (g_ln_audio_download_cancel) {
+ printf("[SSE] download canceled by user\n");
+ break;
+ }
HTTPC_RESULT recv_res = HttpClientRecvResponse(&client, &client_data);
if (recv_res == HTTP_SUCCESS || recv_res == HTTP_EAGAIN)
{
while (fgets(line, sizeof(line), fp))
{
+ if (g_ln_audio_download_cancel) {
+ printf("[SSE] download canceled by user\n");
+ break;
+ }
+
line_count++;
// 去除行尾的换行符
}
else if (mail.cmd == LN_HTTP_SERVICE_CMD_GET_USER_ID)
{
- printf("LN_HTTP_SERVICE_CMD_GET_USER_ID:获取设备绑定用户ID\n");
- ln_device_get_user_id_http(uapi_ln_get_device_id(), uapi_ln_get_device_token());
+ LnDeviceBindStatus st = LN_DEVICE_BIND_STATUS_ERROR;
+ (void)uapi_ln_device_get_user_id(&st, g_ln_device_user_id, sizeof(g_ln_device_user_id));
+ /* 将内部状态映射为上层更直观的 1/0/ERRCODE_FAIL */
+ if (st == LN_DEVICE_BIND_STATUS_BOUND) {
+ g_ln_is_device_bound = 1;
+ } else if (st == LN_DEVICE_BIND_STATUS_UNBOUND) {
+ g_ln_is_device_bound = 0;
+ } else {
+ g_ln_is_device_bound = ERRCODE_FAIL;
+ g_ln_device_user_id[0] = '\0';
+ }
+ printf("LN_HTTP_SERVICE_CMD_GET_USER_ID:获取设备绑定用户ID: %s\n", uapi_ln_get_user_id());
+ printf("LN_HTTP_SERVICE_CMD_GET_USER_ID:获取设备绑定用户ID状态: %d\n", uapi_ln_is_device_bound());
}
else if (mail.cmd == LN_HTTP_SERVICE_CMD_OTA_GET_TASK)
{
else if (mail.cmd == LN_HTTP_SERVICE_CMD_AUDIO_PAUSE)
{
printf("LN_HTTP_SERVICE_CMD_AUDIO_PAUSE:暂停播放\n");
- #ifdef BOARD_N9
- if (ln_story_player_simple_pause())
- {
- printf("Playback paused successfully\n");
- }
- else
- {
- printf("Failed to pause playback\n");
- }
- #endif
+ // #ifdef BOARD_N9
+ // if (ln_story_player_simple_pause())
+ // {
+ // printf("Playback paused successfully\n");
+ // }
+ // else
+ // {
+ // printf("Failed to pause playback\n");
+ // }
+ // #endif
+ uapi_ln_audio_download_is_cancel(1);
}
else if (mail.cmd == LN_HTTP_SERVICE_CMD_AUDIO_RESUME)
{
printf("LN_HTTP_SERVICE_CMD_AUDIO_RESUME:恢复播放\n");
- #ifdef BOARD_N9
- if (ln_story_player_simple_resume())
- {
- printf("Playback resumed successfully\n");
- }
- else
- {
- printf("Failed to resume playback\n");
- }
- #endif
+ // #ifdef BOARD_N9
+ // if (ln_story_player_simple_resume())
+ // {
+ // printf("Playback resumed successfully\n");
+ // }
+ // else
+ // {
+ // printf("Failed to resume playback\n");
+ // }
+ // #endif
+ uapi_ln_audio_download_is_cancel(0);
}
if (mail.data != NULL)
{
char md5[65]; /* MD5(来自任务),仅状态为COMPLETED时有效 */
} OtaDownloadMsg;
+/* 设备绑定状态 */
+typedef enum
+{
+ LN_DEVICE_BIND_STATUS_UNBOUND = 0, /* 未绑定,data 为 null */
+ LN_DEVICE_BIND_STATUS_BOUND = 1, /* 已绑定,返回有效用户ID */
+ LN_DEVICE_BIND_STATUS_AUTH_FAILURE = 2, /* 认证失败(Token 无效或过期)*/
+ LN_DEVICE_BIND_STATUS_ERROR = 3 /* 其他错误(网络/解析等)*/
+} LnDeviceBindStatus;
+
/* 初始化 LN HTTP 服务 */
errcode_t uapi_ln_http_server_init(void);
/* 通用发送接口:向 LN HTTP 发送队列投递消息 */
const char *uapi_ln_get_speechchat_audio_path(void);
/* 获取选择故事录音音频路径 */
const char *uapi_ln_get_story_choose_audio_path(void);
+/* 音频下载/播放控制 */
+void uapi_ln_audio_download_is_cancel(int status);
+/* 获取是否绑定用户 */
+uint32_t uapi_ln_is_device_bound(void);
+/* 获取设备绑定用户ID */
+const char* uapi_ln_get_user_id(void);
/* ============================== 共享全局状态(跨文件) ============================== */
extern char *g_device_token;
uint32_t ln_token_is_expired(int *expired_out);
/* 主动刷新设备Token(调用HTTP接口),并落盘保存 */
uint32_t uapi_ln_refresh_device_token(void);
+uint32_t uapi_ln_device_get_user_id(LnDeviceBindStatus *out_status, char *out_user_id, uint32_t out_user_id_len);
/* ============================== 各接口函数声明 ============================== */
uint32_t ln_get_device_token_http(const char *device_id,
#include "ln_api.h"
#include <stdio.h>
+#include <string.h>
+#include <cJSON.h>
// 获取设备角色信息
uint32_t ln_device_get_character_http(const char *device_id,
HttpClientClse(&client);
return ERRCODE_SUCC;
}
+
+/*
+ * 简单实用的同步接口:获取设备绑定用户ID
+ * - LN_DEVICE_BIND_STATUS_BOUND: 已绑定,out_user_id 返回用户ID
+ * - LN_DEVICE_BIND_STATUS_UNBOUND: 未绑定
+ * - LN_DEVICE_BIND_STATUS_AUTH_FAILURE: 认证失败
+ * - LN_DEVICE_BIND_STATUS_ERROR: 其他错误
+ */
+uint32_t uapi_ln_device_get_user_id(LnDeviceBindStatus *out_status, char *out_user_id, uint32_t out_user_id_len)
+{
+ if (out_status == NULL || out_user_id == NULL || out_user_id_len == 0)
+ {
+ return ERRCODE_FAIL;
+ }
+ *out_status = LN_DEVICE_BIND_STATUS_ERROR;
+ out_user_id[0] = '\0';
+
+ const char *device_id = uapi_ln_get_device_id();
+ const char *token = uapi_ln_get_device_token();
+ if (!device_id || !token)
+ {
+ *out_status = LN_DEVICE_BIND_STATUS_AUTH_FAILURE;
+ return ERRCODE_FAIL;
+ }
+
+/* 发起请求 */
+#ifdef SUPPORT_CAT1_SERVICE
+ cat1_api_add_cat1_vote();
+ cat1_api_wakeup_cat1(1);
+ cat1_api_keep_wakeup(1);
+ cat1_api_disable_cat1_cmd(1);
+ LOS_Msleep(200);
+#endif
+
+ HttpClient client = (HttpClient){0};
+ HttpClientData clientData = (HttpClientData){0};
+ client.isHttp = false;
+ client.remotePort = 443;
+
+ char header_buf[512];
+ int hdr = snprintf(header_buf, sizeof(header_buf),
+ "deviceId: %s\r\n"
+ "deviceToken: %s\r\n"
+ "Content-Type: application/json\r\n",
+ device_id, token);
+ if (hdr <= 0 || hdr >= (int)sizeof(header_buf))
+ {
+ return ERRCODE_FAIL;
+ }
+ HttpClientSetCustomHeader(&client, header_buf);
+
+ HTTPC_RESULT prep = ln_http_prepare_auto(&clientData, 2048, LN_CLIENT_PREPARE_SIZE);
+ if (prep != HTTP_SUCCESS)
+ {
+ return ERRCODE_FAIL;
+ }
+
+ HTTPC_RESULT res = HttpClientGetRequest(&client, LN_DEVICE_GET_USER_ID_URL, &clientData);
+ if (res != HTTP_SUCCESS)
+ {
+ HttpClientUnprepare(&clientData);
+ HttpClientClse(&client);
+ return ERRCODE_FAIL;
+ }
+
+ int code = HttpClientGetResponseCode(&client);
+ if (code == 200)
+ {
+ /* 成功响应:解析JSON,判断 data 是否为 null 或字符串 */
+ if (clientData.responseBuf && clientData.responseBufLen > 0)
+ {
+ int real_len = clientData.responseContentLen > 0 ? clientData.responseContentLen : clientData.responseBufLen;
+ char *buf = (char *)malloc((size_t)real_len + 1);
+ if (buf)
+ {
+ memcpy(buf, clientData.responseBuf, (size_t)real_len);
+ buf[real_len] = '\0';
+ cJSON *root = cJSON_Parse(buf);
+ if (root)
+ {
+ cJSON *data = cJSON_GetObjectItem(root, "data");
+ if (data == NULL || cJSON_IsNull(data))
+ {
+ *out_status = LN_DEVICE_BIND_STATUS_UNBOUND;
+ }
+ else if (cJSON_IsString(data) && data->valuestring && data->valuestring[0])
+ {
+ *out_status = LN_DEVICE_BIND_STATUS_BOUND;
+ strncpy(out_user_id, data->valuestring, out_user_id_len - 1);
+ out_user_id[out_user_id_len - 1] = '\0';
+ }
+ else
+ {
+ *out_status = LN_DEVICE_BIND_STATUS_ERROR;
+ }
+ cJSON_Delete(root);
+ }
+ free(buf);
+ }
+ }
+ }
+ else if (code == 401)
+ {
+ /* 认证失败 */
+ *out_status = LN_DEVICE_BIND_STATUS_AUTH_FAILURE;
+ }
+ else
+ {
+ *out_status = LN_DEVICE_BIND_STATUS_ERROR;
+ }
+
+ HttpClientUnprepare(&clientData);
+ HttpClientClse(&client);
+ return ERRCODE_SUCC;
+}
WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"ln_mkdir_p: path is NULL");
return;
}
- (void)mkdir(LN_TOKEN_DIR, 0755);
+ (void)mkdir(path, 0755);
}
/* 保存设备Token到本地 */
WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"ln_token_save_json: token is NULL");
return ERRCODE_FAIL;
}
+ ln_mkdir_p(LN_DATA_PATH);
ln_mkdir_p(LN_TOKEN_DIR);
/* 生成create_time字符串 */
if (text == NULL)
return ERRCODE_FAIL;
- FILE *fp = fopen(LN_TOKEN_FILE, "wb");
+ FILE *fp = fopen(LN_TOKEN_FILE, "wb+");
if (!fp)
{
WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"ln_token_save_json: fopen failed");