talk.lua 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686
  1. -- talk.lua
  2. local talk = {}
  3. dnsproxy = require("dnsproxy")
  4. dhcpsrv = require("dhcpsrv")
  5. httpplus = require("httpplus")
  6. local run_state = false
  7. local airaudio = require "airaudio"
  8. local input_method = require "InputMethod"
  9. local input_key = false
  10. -- 初始化fskv
  11. AIRTALK_TASK_NAME = "airtalk_task"
  12. USER_TASK_NAME = "user"
  13. MSG_CONNECT_ON_IND = 0
  14. MSG_CONNECT_OFF_IND = 1
  15. MSG_AUTH_IND = 2
  16. MSG_SPEECH_ON_IND = 3
  17. MSG_SPEECH_OFF_IND = 4
  18. MSG_SPEECH_CONNECT_TO = 5
  19. MSG_PERSON_SPEECH_TEST_START = 20
  20. MSG_GROUP_SPEECH_TEST_START = 21
  21. MSG_SPEECH_STOP_TEST_END = 22
  22. MSG_READY = 10
  23. MSG_NOT_READY = 11
  24. MSG_TYPE = 12
  25. -- 新增消息类型
  26. MSG_ADDRESS_LIST_OPEN = 30
  27. MSG_ADDRESS_LIST_BACK = 31
  28. MSG_ADDRESS_LIST_PREV = 32
  29. MSG_ADDRESS_LIST_NEXT = 33
  30. MSG_ADDRESS_LIST_SELECT = 34
  31. SP_T_NO_READY = 0 -- 离线状态无法对讲
  32. SP_T_IDLE = 1 -- 对讲空闲状态
  33. SP_T_CONNECTING = 2 -- 主动发起对讲
  34. SP_T_CONNECTED = 3 -- 对讲中
  35. SUCC = "success"
  36. local speech_topic = nil
  37. local mqtt_host = "lbsmqtt.openluat.com"
  38. local mqtt_port = 1886
  39. local mqtt_isssl = false
  40. local client_id = nil
  41. local user_name = "mqtt_hz_test_1"
  42. local password = "Ck8WpNCp"
  43. local mqttc = nil
  44. local message = ""
  45. local event = ""
  46. local talk_state = ""
  47. local mqttc = nil
  48. local g_state = SP_T_NO_READY --device状态
  49. local g_mqttc = nil --mqtt客户端
  50. local g_local_id --本机ID
  51. local g_remote_id --对端ID
  52. local g_s_type --对讲的模式,字符串形式的
  53. local g_s_topic --对讲用的topic
  54. local g_s_mode --对讲的模式
  55. local g_dev_list = {} --对讲列表
  56. -- 新增通讯录相关变量
  57. local address_list_page = 1 -- 通讯录当前页码
  58. local address_list_max_page = 1 -- 通讯录最大页码
  59. local current_page = "main" -- 当前页面状态
  60. local contacts_per_page = 8 -- 每页显示的联系人数量
  61. local function auth()
  62. if g_state == SP_T_NO_READY then
  63. g_mqttc:publish("ctrl/uplink/" .. g_local_id .."/0001", json.encode({["key"] = PRODUCT_KEY, ["device_type"] = 1}))
  64. end
  65. end
  66. local function heart()
  67. -- if g_state == SP_T_CONNECTED then
  68. log.info("心跳上报")
  69. g_mqttc:publish("ctrl/uplink/" .. g_local_id .."/0005", json.encode({["csq"] = mobile.csq(), ["battery"] = 100}))
  70. -- end
  71. end
  72. --对讲开始,topic,ssrc,采样率(8K或者16K)这3个参数都有了之后就能进行对讲了,可以通过其他协议传入
  73. local function speech_on(ssrc, sample)
  74. g_state = SP_T_CONNECTED
  75. g_mqttc:subscribe(g_s_topic)
  76. airtalk.set_topic(g_s_topic)
  77. airtalk.set_ssrc(ssrc)
  78. log.info("对讲模式", g_s_mode)
  79. airtalk.speech(true, g_s_mode, sample)
  80. sys.sendMsg(AIRTALK_TASK_NAME, MSG_SPEECH_ON_IND, true)
  81. sys.timerStopAll(wait_speech_to)
  82. log.info("对讲接通,可以说话了")
  83. end
  84. --对讲结束
  85. local function speech_off(need_upload, need_ind)
  86. if g_state == SP_T_CONNECTED then
  87. g_mqttc:unsubscribe(g_s_topic)
  88. airtalk.speech(false)
  89. g_s_topic = nil
  90. end
  91. g_state = SP_T_IDLE
  92. sys.timerStopAll(auth)
  93. sys.timerStopAll(heart)
  94. sys.timerStopAll(wait_speech_to)
  95. log.info("对讲断开了")
  96. if need_upload then
  97. g_mqttc:publish("ctrl/uplink/" .. g_local_id .."/0004", json.encode({["to"] = g_remote_id}))
  98. end
  99. if need_ind then
  100. sys.sendMsg(AIRTALK_TASK_NAME, MSG_SPEECH_OFF_IND, true)
  101. end
  102. end
  103. function wait_speech_to()
  104. log.info("主动请求对讲超时无应答")
  105. speech_off(true, false)
  106. end
  107. local function analyze_v1(cmd, topic, obj)
  108. if cmd == "8005" or cmd == "8004" then -- 对讲心跳保持和结束对讲的应答不做处理
  109. return
  110. end
  111. if cmd == "8003" then -- 请求对讲应答
  112. if g_state ~= SP_T_CONNECTING then --没有发起对讲请求
  113. log.error("state", g_state, "need", SP_T_CONNECTING)
  114. return
  115. else
  116. if obj and obj["result"] == SUCC and g_s_topic == obj["topic"]then --完全正确,开始对讲
  117. speech_on(obj["ssrc"], obj["audio_code"] == "amr-nb" and 8000 or 16000)
  118. return
  119. else
  120. log.info(obj["result"], obj["topic"], g_s_topic)
  121. sys.sendMsg(AIRTALK_TASK_NAME, MSG_SPEECH_ON_IND, false) --有异常,无法对讲
  122. end
  123. end
  124. g_s_topic = nil
  125. g_state = SP_T_IDLE
  126. return
  127. end
  128. local new_obj = nil
  129. if cmd == "0102" then -- 对端打过来
  130. if obj and obj["topic"] and obj["ssrc"] and obj["audio_code"] and obj["type"] then
  131. if g_state ~= SP_T_IDLE then -- 空闲状态下才可以进入对讲状态
  132. log.error("state", g_state, "need", SP_T_IDLE)
  133. new_obj = {["result"] = "failed", ["topic"] = obj["topic"], ["info"] = "device is busy"}
  134. else
  135. if obj["type"] == "one-on-one" then -- 1对1对讲
  136. local from = string.match(obj["topic"], "audio/.*/(.*)/.*")
  137. if from then
  138. log.info("remote id ", from)
  139. g_s_topic = obj["topic"]
  140. g_remote_id = from
  141. new_obj = {["result"] = SUCC, ["topic"] = obj["topic"], ["info"] = ""}
  142. g_s_type = "one-on-one"
  143. g_s_mode = airtalk.MODE_PERSON
  144. speech_on(obj["ssrc"], obj["audio_code"] == "amr-nb" and 8000 or 16000)
  145. else
  146. new_obj = {["result"] = "failed", ["topic"] = obj["topic"], ["info"] = "topic error"}
  147. end
  148. elseif obj["type"] == "broadcast" then -- 1对多对讲
  149. g_s_topic = obj["topic"]
  150. new_obj = {["result"] = SUCC, ["topic"] = obj["topic"], ["info"] = ""}
  151. g_s_mode = airtalk.MODE_GROUP_LISTENER
  152. g_s_type = "broadcast"
  153. speech_on(obj["ssrc"], obj["audio_code"] == "amr-nb" and 8000 or 16000)
  154. end
  155. end
  156. else
  157. new_obj = {["result"] = "failed", ["topic"] = obj["topic"], ["info"] = "json info error"}
  158. end
  159. g_mqttc:publish("ctrl/uplink/" .. g_local_id .."/8102", json.encode(new_obj))
  160. return
  161. end
  162. if cmd == "0103" then --对端挂断
  163. if g_state == SP_T_IDLE then
  164. new_obj = {["result"] = "failed", ["info"] = "no speech"}
  165. else
  166. if obj and obj["type"] == g_s_type then
  167. new_obj = {["result"] = SUCC, ["info"] = ""}
  168. speech_off(false, true)
  169. else
  170. new_obj = {["result"] = "failed", ["info"] = "type mismatch"}
  171. end
  172. end
  173. g_mqttc:publish("ctrl/uplink/" .. g_local_id .."/8103", json.encode(new_obj))
  174. return
  175. end
  176. if cmd == "0101" then --更新设备列表
  177. if obj then
  178. g_dev_list = obj["dev_list"]
  179. -- 计算通讯录最大页码
  180. address_list_max_page = math.ceil(#g_dev_list / contacts_per_page)
  181. if address_list_max_page == 0 then
  182. address_list_max_page = 1
  183. end
  184. -- for i=1,#g_dev_list do
  185. -- log.info(g_dev_list[i]["id"],g_dev_list[i]["name"])
  186. -- end
  187. new_obj = {["result"] = SUCC, ["info"] = ""}
  188. else
  189. new_obj = {["result"] = "failed", ["info"] = "json info error"}
  190. end
  191. g_mqttc:publish("ctrl/uplink/" .. g_local_id .."/8101", json.encode(new_obj))
  192. return
  193. end
  194. if cmd == "8001" then
  195. if obj and obj["result"] == SUCC then
  196. g_mqttc:publish("ctrl/uplink/" .. g_local_id .."/0002","") -- 更新列表
  197. else
  198. sys.sendMsg(AIRTALK_TASK_NAME, MSG_AUTH_IND, false, "鉴权失败" .. obj["info"])
  199. end
  200. return
  201. end
  202. if cmd == "8002" then
  203. if obj and obj["result"] == SUCC then --收到设备列表更新应答,才能认为相关网络服务准备好了
  204. g_dev_list = obj["dev_list"]
  205. -- 计算通讯录最大页码
  206. address_list_max_page = math.ceil(#g_dev_list / contacts_per_page)
  207. if address_list_max_page == 0 then
  208. address_list_max_page = 1
  209. end
  210. for i=1,#g_dev_list do
  211. log.info(g_dev_list[i]["id"],g_dev_list[i]["name"])
  212. end
  213. g_state = SP_T_IDLE
  214. sys.sendMsg(AIRTALK_TASK_NAME, MSG_AUTH_IND, true) --完整登录流程结束
  215. else
  216. sys.sendMsg(AIRTALK_TASK_NAME, MSG_AUTH_IND, false, "更新设备列表失败")
  217. end
  218. return
  219. end
  220. end
  221. local function mqtt_cb(mqttc, event, topic, payload)
  222. log.info(event, topic)
  223. local msg,data,obj
  224. if event == "conack" then
  225. sys.sendMsg(AIRTALK_TASK_NAME, MSG_CONNECT_ON_IND) --mqtt连上了,开始自定义的鉴权流程
  226. g_mqttc:subscribe("ctrl/downlink/" .. g_local_id .. "/#")--单主题订阅
  227. elseif event == "suback" then
  228. if g_state == SP_T_NO_READY then
  229. if topic then
  230. auth()
  231. else
  232. sys.sendMsg(AIRTALK_TASK_NAME, MSG_AUTH_IND, false, "订阅失败" .. "ctrl/downlink/" .. g_local_id .. "/#")
  233. end
  234. elseif g_state == SP_T_CONNECTED then
  235. if not topic then
  236. speech_off(false, true)
  237. end
  238. end
  239. elseif event == "recv" then
  240. local result = string.match(topic, g_dl_topic)
  241. if result then
  242. local obj,res,err = json.decode(payload)
  243. analyze_v1(result, topic, obj)
  244. end
  245. result = nil
  246. data = nil
  247. obj = nil
  248. elseif event == "sent" then
  249. -- log.info("mqtt", "sent", "pkgid", data)
  250. elseif event == "disconnect" then
  251. speech_off(false, true)
  252. g_state = SP_T_NO_READY
  253. elseif event == "error" then
  254. end
  255. end
  256. local function task_cb(msg)
  257. if msg[1] == MSG_SPEECH_CONNECT_TO then
  258. speech_off(true,false)
  259. else
  260. log.info("未处理消息", msg[1], msg[2], msg[3], msg[4])
  261. end
  262. end
  263. local function airtalk_event_cb(event, param)
  264. log.info("airtalk event", event, param)
  265. if event == airtalk.EVENT_ERROR then
  266. if param == airtalk.ERROR_NO_DATA then
  267. log.error("长时间没有收到音频数据")
  268. speech_off(true, true)
  269. end
  270. end
  271. end
  272. local function airtalk_mqtt_task()
  273. local msg,data,obj,online,num,res
  274. --g_local_id也可以自己设置
  275. g_dl_topic = "ctrl/downlink/" .. g_local_id .. "/(%w%w%w%w)"
  276. sys.timerLoopStart(next_auth, 900000)
  277. g_mqttc = mqtt.create(nil, "mqtt.airtalk.luatos.com", 1883, false, {rxSize = 32768})
  278. airtalk.config(airtalk.PROTOCOL_MQTT, g_mqttc, 200) -- 缓冲至少200ms播放
  279. airtalk.on(airtalk_event_cb)
  280. airtalk.start()
  281. g_mqttc:auth(g_local_id,g_local_id,mobile.muid()) -- g_local_id必填,其余选填
  282. g_mqttc:keepalive(240) -- 默认值240s
  283. g_mqttc:autoreconn(true, 15000) -- 自动重连机制
  284. g_mqttc:debug(false)
  285. g_mqttc:on(mqtt_cb)
  286. log.info("设备信息", g_local_id, mobile.muid())
  287. -- mqttc自动处理重连, 除非自行关闭
  288. g_mqttc:connect()
  289. online = false
  290. while true do
  291. msg = sys.waitMsg(AIRTALK_TASK_NAME, MSG_CONNECT_ON_IND) --等服务器连上
  292. log.info("connected")
  293. while not online do
  294. msg = sys.waitMsg(AIRTALK_TASK_NAME, MSG_AUTH_IND, 30000) --登录流程不应该超过30秒
  295. if type(msg) == 'table' then
  296. online = msg[2]
  297. if online then
  298. sys.timerLoopStart(auth, 3600000) --鉴权通过则60分钟后尝试重新鉴权
  299. else
  300. log.info(msg[3])
  301. sys.timerLoopStart(auth, 300000) --5分钟后重新鉴权
  302. end
  303. else
  304. auth() --30秒鉴权无效后重新鉴权
  305. end
  306. end
  307. log.info("对讲管理平台已连接")
  308. while online do
  309. msg = sys.waitMsg(AIRTALK_TASK_NAME)
  310. if type(msg) == 'table' and type(msg[1]) == "number" then
  311. if msg[1] == MSG_PERSON_SPEECH_TEST_START then
  312. if g_state ~= SP_T_IDLE then
  313. log.info("正在对讲无法开始")
  314. else
  315. log.info("匹配输入的设备号是在设备列表中")
  316. res = false
  317. for i=1,#g_dev_list do
  318. res = string.match(g_dev_list[i]["id"], "(%w%w%w%w%w%w%w%w%w%w%w%w%w%w%w)")
  319. if res and res == speech_topic then
  320. res = true
  321. break
  322. end
  323. end
  324. if res then
  325. log.info("向", speech_topic, "主动发起对讲")
  326. g_state = SP_T_CONNECTING
  327. g_remote_id = speech_topic
  328. g_s_mode = airtalk.MODE_PERSON
  329. g_s_type = "one-on-one"
  330. g_s_topic = "audio/" .. g_local_id .. "/" .. g_remote_id .. "/" .. (string.sub(tostring(mcu.ticks()), -4, -1))
  331. g_mqttc:publish("ctrl/uplink/" .. g_local_id .."/0003", json.encode({["topic"] = g_s_topic, ["type"] = g_s_type}))
  332. sys.timerStart(wait_speech_to, 15000)
  333. else
  334. log.info("找不到有效的设备ID")
  335. end
  336. end
  337. elseif msg[1] == MSG_GROUP_SPEECH_TEST_START then
  338. if g_state ~= SP_T_IDLE then
  339. log.info("正在对讲无法开始")
  340. else
  341. log.info("测试一下1对多对讲功能")
  342. g_remote_id = "all"
  343. g_state = SP_T_CONNECTING
  344. g_s_mode = airtalk.MODE_GROUP_SPEAKER
  345. g_s_type = "broadcast"
  346. g_s_topic = "audio/" .. g_local_id .. "/all/" .. (string.sub(tostring(mcu.ticks()), -4, -1))
  347. g_mqttc:publish("ctrl/uplink/" .. g_local_id .."/0003", json.encode({["topic"] = g_s_topic, ["type"] = g_s_type}))
  348. sys.timerStart(wait_speech_to, 15000)
  349. end
  350. elseif msg[1] == MSG_SPEECH_STOP_TEST_END then
  351. if g_state ~= SP_T_CONNECTING and g_state ~= SP_T_CONNECTED then
  352. log.info("没有对讲", g_state)
  353. else
  354. log.info("主动断开对讲")
  355. speech_off(true, false)
  356. end
  357. elseif msg[1] == MSG_SPEECH_ON_IND then
  358. if msg[2] then
  359. log.info("对讲接通")
  360. else
  361. log.info("对讲断开")
  362. end
  363. elseif msg[1] == MSG_CONNECT_OFF_IND then
  364. log.info("connect", msg[2])
  365. online = msg[2]
  366. end
  367. obj = nil
  368. else
  369. log.info(type(msg), type(msg[1]))
  370. end
  371. msg = nil
  372. end
  373. online = false
  374. end
  375. end
  376. function airtalk_mqtt_init()
  377. sys.taskInitEx(airtalk_mqtt_task, AIRTALK_TASK_NAME, task_cb)
  378. end
  379. local function airtalk_event_cb(event, param)
  380. log.info("talk event", event, param)
  381. event = event
  382. end
  383. -- MQTT回调函数
  384. local function mqtt_cb(mqtt_client, event, data, payload)
  385. log.info("mqtt", "event", event, mqtt_client, data, payload)
  386. -- 连接成功时订阅主题
  387. end
  388. local function task_cb(msg)
  389. log.info("未处理消息", msg[1], msg[2], msg[3], msg[4])
  390. if msg[1] == MSG_SPEECH_IND then
  391. elseif msg[1] == MSG_NOT_READY then
  392. test_ready = false
  393. msg = sys.waitMsg(USER_TASK_NAME, MSG_TYPE)
  394. end
  395. end
  396. local function init_talk()
  397. log.info("init_call")
  398. airaudio.init()
  399. airtalk_mqtt_init()
  400. sys.timerLoopStart(heart, 10000)
  401. local msg
  402. while true do
  403. msg = sys.waitMsg(USER_TASK_NAME, MSG_TYPE)
  404. if msg[2] then -- true powerkey false boot key
  405. sys.sendMsg(AIRTALK_TASK_NAME, MSG_GROUP_SPEECH_TEST_START)
  406. else
  407. sys.sendMsg(AIRTALK_TASK_NAME, MSG_PERSON_SPEECH_TEST_START)
  408. end
  409. msg = sys.waitMsg(USER_TASK_NAME, MSG_TYPE)
  410. sys.sendMsg(AIRTALK_TASK_NAME, MSG_SPEECH_STOP_TEST_END)
  411. end
  412. end
  413. -- 输入法回调函数
  414. local function submit_callback(input_text)
  415. if input_text and #input_text > 0 then
  416. speech_topic = input_text
  417. fskv.set("talk_number", input_text) -- 保存对讲号码到fskv
  418. log.info("talk", "对讲号码:", fskv.get("talk_number"))
  419. input_key = false
  420. end
  421. end
  422. -- 绘制通讯录页面
  423. local function draw_address_list()
  424. lcd.clear(_G.bkcolor)
  425. -- 绘制返回按钮 (左上角)
  426. lcd.showImage(10, 10, "/luadb/back.jpg")
  427. -- 绘制标题 (居中)
  428. lcd.drawStr(120, 30, "通讯录")
  429. -- 计算当前页的联系人起始和结束索引
  430. local start_index = (address_list_page - 1) * contacts_per_page + 1
  431. local end_index = math.min(start_index + contacts_per_page - 1, #g_dev_list)
  432. -- 绘制联系人列表
  433. local y_pos = 78
  434. for i = start_index, end_index do
  435. local contact = g_dev_list[i]
  436. -- 绘制ID
  437. lcd.drawStr(10, y_pos, "ID: " .. (contact["id"] or ""))
  438. -- 绘制名称
  439. lcd.drawStr(10, y_pos + 15, "名称: " .. (contact["name"] or "未知"))
  440. -- 绘制分隔线
  441. lcd.drawLine(5, y_pos + 35-12, 315, y_pos + 35-12)
  442. y_pos = y_pos + 40 -- 每个联系人占40像素高度
  443. end
  444. -- 绘制翻页按钮 (底部居中)
  445. local page_btn_y = 412
  446. if address_list_page > 1 then
  447. lcd.drawStr(50, page_btn_y, "上一页")
  448. end
  449. if address_list_page < address_list_max_page then
  450. lcd.drawStr(220, page_btn_y, "下一页")
  451. end
  452. -- 绘制页码信息 (底部居中)
  453. lcd.drawStr(140, page_btn_y, address_list_page .. "/" .. address_list_max_page)
  454. -- 如果正在通话,绘制停止按钮 (底部居中)
  455. if g_state == SP_T_CONNECTED then
  456. lcd.fill(120, 435, 200, 465,0xF061) -- 绘制停止按钮边框
  457. lcd.drawStr(130, 462, "停止通话")
  458. end
  459. lcd.flush()
  460. end
  461. function talk.run()
  462. log.info("talk.run",airtalk.PROTOCOL_DEMO_MQTT_16K)
  463. lcd.setFont(lcd.font_opposansm12_chinese)
  464. run_state = true
  465. g_local_id = mobile.imei()
  466. sys.taskInitEx(init_talk, USER_TASK_NAME, task_cb)
  467. speech_topic = fskv.get("talk_number")
  468. log.info("get speech_topic",speech_topic)
  469. while run_state do
  470. sys.wait(100)
  471. if input_method.is_active() then
  472. input_method.periodic_refresh()
  473. else
  474. if current_page == "main" then
  475. lcd.clear(_G.bkcolor)
  476. if speech_topic == nil then
  477. lcd.drawStr(0, 80, "输入任意手机号,并保证所有终端/平台一致")
  478. lcd.drawStr(0, 100, "方案介绍:airtalk.luatos.com")
  479. lcd.drawStr(0, 120, "平台端网址:airtalk.openluat.com/talk/")
  480. lcd.drawStr(0, 140, "本机ID:" .. g_local_id)
  481. lcd.showImage(32, 250, "/luadb/input_topic.jpg")
  482. lcd.showImage(32, 350, "/luadb/broadcast.jpg")
  483. lcd.showImage(104, 400, "/luadb/stop.jpg")
  484. else
  485. lcd.drawStr(0, 80, "对讲测试,测试topic:"..speech_topic )
  486. lcd.drawStr(0, 100, "方案介绍:airtalk.luatos.com")
  487. lcd.drawStr(0, 120, "平台端网址:airtalk.openluat.com/talk/")
  488. lcd.drawStr(0, 140, "所有终端或者网页都要使用同一个topic")
  489. lcd.drawStr(0, 160, talk_state)
  490. lcd.drawStr(0, 180, "事件:" .. event)
  491. lcd.drawStr(0, 200, "本机ID:" .. g_local_id)
  492. -- 显示输入法入口按钮
  493. lcd.showImage(32, 250, "/luadb/input_topic.jpg")
  494. lcd.showImage(175, 300, "/luadb/datacall.jpg")
  495. lcd.showImage(32, 300, "/luadb/broadcast.jpg")
  496. lcd.showImage(104, 400, "/luadb/stop.jpg")
  497. lcd.showImage(0, 448, "/luadb/Lbottom.jpg")
  498. end
  499. -- 显示通讯录按钮 (位置x10,y250)
  500. lcd.showImage(175, 250, "/luadb/addresslist.jpg")
  501. lcd.showImage(0,0,"/luadb/back.jpg")
  502. lcd.flush()
  503. elseif current_page == "address_list" then
  504. draw_address_list()
  505. end
  506. end
  507. if not run_state then
  508. return true
  509. end
  510. end
  511. end
  512. local function stop_talk()
  513. talk_state = "停止对讲"
  514. sys.sendMsg(AIRTALK_TASK_NAME, MSG_SPEECH_STOP_TEST_END) -- 停止对讲
  515. end
  516. local function start_talk()
  517. talk_state = "一对一通话开始"
  518. sys.sendMsg(AIRTALK_TASK_NAME, MSG_PERSON_SPEECH_TEST_START) -- 开始一对一流量电话
  519. end
  520. local function start_broadcast()
  521. talk_state = "语音采集上传中,正在广播"
  522. sys.sendMsg(AIRTALK_TASK_NAME, MSG_GROUP_SPEECH_TEST_START) -- 开始广播
  523. end
  524. local function start_input()
  525. input_key = true
  526. input_method.init(false, "talk", submit_callback) -- 直接传递函数
  527. end
  528. -- 打开通讯录
  529. local function open_address_list()
  530. current_page = "address_list"
  531. address_list_page = 1
  532. end
  533. -- 返回主页面
  534. local function back_to_main()
  535. current_page = "main"
  536. end
  537. -- 选择联系人
  538. local function select_contact(index)
  539. local contact_index = (address_list_page - 1) * contacts_per_page + index
  540. if contact_index <= #g_dev_list then
  541. local contact = g_dev_list[contact_index]
  542. if contact and contact["id"] then
  543. speech_topic = contact["id"]
  544. fskv.set("talk_number", speech_topic)
  545. start_talk()
  546. -- 保持在通讯录页面,但显示停止按钮
  547. end
  548. end
  549. end
  550. -- 处理通讯录页面的触摸事件
  551. local function handle_address_list_touch(x, y)
  552. -- 返回按钮区域 (左上角)
  553. if x > 10 and x < 50 and y > 10 and y < 50 then
  554. back_to_main()
  555. return
  556. end
  557. -- 上一页按钮区域 (底部左侧)
  558. if address_list_page > 1 and x > 40 and x < 90 and y > 390 and y < 420 then
  559. address_list_page = address_list_page - 1
  560. return
  561. end
  562. -- 下一页按钮区域 (底部右侧)
  563. if address_list_page < address_list_max_page and x > 210 and x < 260 and y > 390 and y < 420 then
  564. address_list_page = address_list_page + 1
  565. return
  566. end
  567. -- 停止通话按钮区域 (底部居中)
  568. if g_state == SP_T_CONNECTED and x > 120 and x < 200 and y > 435 and y < 465 then
  569. stop_talk()
  570. return
  571. end
  572. -- 联系人选择区域 (60-380像素高度)
  573. if y >= 60 and y <= 380 then
  574. local contact_index = math.floor((y - 60) / 40) + 1
  575. if contact_index >= 1 and contact_index <= contacts_per_page then
  576. select_contact(contact_index)
  577. end
  578. end
  579. end
  580. function talk.tp_handal(x, y, event)
  581. if input_key then
  582. input_method.process_touch(x, y)
  583. else
  584. if current_page == "main" then
  585. if x > 0 and x < 80 and y > 0 and y < 80 then
  586. run_state = false
  587. elseif x > 32 and x < 133 and y > 250 and y < 295 then
  588. sysplus.taskInitEx(start_input,"start_input")
  589. elseif x > 173 and x < 284 and y > 300 and y < 345 then
  590. sysplus.taskInitEx(start_talk, "start_talk")
  591. elseif x > 32 and x < 133 and y > 300 and y < 345 then
  592. sysplus.taskInitEx(start_broadcast, "start_broadcast")
  593. elseif x > 104 and x < 215 and y > 397 and y < 444 then
  594. sysplus.taskInitEx(stop_talk, "stop_talk")
  595. elseif x > 175 and x < 286 and y > 250 and y < 295 then -- 通讯录按钮
  596. sysplus.taskInitEx(open_address_list, "open_address_list")
  597. end
  598. elseif current_page == "address_list" then
  599. handle_address_list_touch(x, y)
  600. end
  601. end
  602. end
  603. return talk