Du kan inte välja fler än 25 ämnen Ämnen måste starta med en bokstav eller siffra, kan innehålla bindestreck ('-') och vara max 35 tecken långa.

ElinkHealthRingActivity.kt 14KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400
  1. package com.elinkthings.elinkhealthringsdkdemo
  2. import android.app.AlertDialog
  3. import android.content.Context
  4. import android.content.Intent
  5. import android.os.Bundle
  6. import android.view.MenuItem
  7. import android.widget.Button
  8. import androidx.annotation.IdRes
  9. import androidx.lifecycle.lifecycleScope
  10. import androidx.recyclerview.widget.LinearLayoutManager
  11. import androidx.recyclerview.widget.RecyclerView
  12. import com.elinkthings.bleotalibrary.listener.OnBleOTAListener
  13. import com.elinkthings.healthring.ElinkHealthRingBleData
  14. import com.elinkthings.healthring.bean.ElinkCheckupRealtimeData
  15. import com.elinkthings.healthring.bean.ElinkRingDeviceStatus
  16. import com.elinkthings.healthring.bean.ElinkRingHistoryData
  17. import com.elinkthings.healthring.config.ElinkCheckupType
  18. import com.elinkthings.healthring.config.ElinkSensorOTAErrorType
  19. import com.elinkthings.healthring.impl.ImplHealthRingResult
  20. import com.elinkthings.healthring.impl.ImplSensorOTA
  21. import com.elinkthings.healthring.utils.toHexString
  22. import com.pingwang.bluetoothlib.bean.BleValueBean
  23. import com.pingwang.bluetoothlib.device.BleDevice
  24. import com.pingwang.bluetoothlib.listener.OnBleVersionListener
  25. import com.pingwang.bluetoothlib.listener.OnCallbackBle
  26. import kotlinx.coroutines.Dispatchers
  27. import kotlinx.coroutines.launch
  28. import kotlinx.coroutines.withContext
  29. import java.io.ByteArrayOutputStream
  30. import java.io.File
  31. import java.io.FileOutputStream
  32. import java.io.InputStream
  33. import java.text.SimpleDateFormat
  34. import java.util.*
  35. /**
  36. * @author suzy
  37. * @date 2024/3/13 16:54
  38. **/
  39. class ElinkHealthRingActivity : ElinkBasePermissionActivity(), OnCallbackBle, OnBleVersionListener,
  40. ImplHealthRingResult {
  41. private val logList = mutableListOf<String>()
  42. private val logAdapter = BleLogAdapter(logList)
  43. private var rvBleLog: RecyclerView? = null
  44. private var ringBleData: ElinkHealthRingBleData? = null
  45. companion object {
  46. private const val EXTRA_MAC = "EXTRA_MAC"
  47. private const val EXTRA_CID = "EXTRA_CID"
  48. private const val EXTRA_VID = "EXTRA_VID"
  49. private const val EXTRA_PID = "EXTRA_PID"
  50. private val SENSOR_OTA_FILES =
  51. arrayOf("Sensor-202312271422-0x0407.bin", "Sensor-202401021530-0x0408.bin")
  52. private val BLE_OTA_FILES =
  53. arrayOf("BR01H1S1.0.0_20230923.img", "BR01H1S1.0.0_20240125.img")
  54. fun start(context: Context, mac: String, cid: Int, vid: Int, pid: Int) =
  55. Intent(context, ElinkHealthRingActivity::class.java).apply {
  56. putExtra(EXTRA_MAC, mac)
  57. putExtra(EXTRA_CID, cid)
  58. putExtra(EXTRA_VID, vid)
  59. putExtra(EXTRA_PID, pid)
  60. context.startActivity(this)
  61. }
  62. }
  63. private val mac: String
  64. get() = intent.getStringExtra(EXTRA_MAC) ?: ""
  65. private val bleData: BleValueBean?
  66. get() {
  67. val cid = intent.getIntExtra(EXTRA_CID, 0)
  68. val vid = intent.getIntExtra(EXTRA_VID, 0)
  69. val pid = intent.getIntExtra(EXTRA_PID, 0)
  70. if (mac.isEmpty()) {
  71. return null
  72. }
  73. return BleValueBean(mac, cid, vid, pid)
  74. }
  75. private val bleDevice: BleDevice?
  76. get() {
  77. return aiLinkBleManager?.getBleDevice(mac)
  78. }
  79. override fun onCreate(savedInstanceState: Bundle?) {
  80. super.onCreate(savedInstanceState)
  81. supportActionBar?.setDisplayHomeAsUpEnabled(true)
  82. setContentView(R.layout.activity_elink_health_ring)
  83. initButton(R.id.btn_health_ring_connect) {
  84. connect()
  85. }
  86. initButton(R.id.btn_health_ring_disconnect) {
  87. aiLinkBleManager?.disconnect(mac)
  88. }
  89. initButton(R.id.btn_health_ring_device_state) {
  90. ringBleData?.getDeviceState()
  91. }
  92. initButton(R.id.btn_health_ring_checkup_duration) {
  93. ringBleData?.getCheckupDuration()
  94. }
  95. initButton(R.id.btn_health_ring_sensor_version) {
  96. ringBleData?.getSensorVersion()
  97. }
  98. initButton(R.id.btn_health_ring_checkup_type) {
  99. ringBleData?.getCheckupType()
  100. }
  101. initButton(R.id.btn_health_ring_auto_checkup) {
  102. ringBleData?.getAutoCheckState()
  103. }
  104. initButton(R.id.btn_health_ring_open_auto_checkup) {
  105. ringBleData?.openAutoCheck()
  106. }
  107. initButton(R.id.btn_health_ring_close_auto_checkup) {
  108. ringBleData?.closeAutoCheck()
  109. }
  110. initButton(R.id.btn_health_ring_close_auto_checkup) {
  111. ringBleData?.closeAutoCheck()
  112. }
  113. initButton(R.id.btn_health_ring_set_checkup_duration) {
  114. showListDialog(getString(R.string.set_checkup_duration), arrayOf("15", "30", "45", "60")) { item, _ ->
  115. ringBleData?.setCheckupDuration(item.toInt())
  116. }
  117. }
  118. initButton(R.id.btn_health_ring_set_checkup_type) {
  119. showListDialog(getString(R.string.set_checkup_type), arrayOf(getString(R.string.checkup_type_complex), getString(R.string.checkup_type_fast))) { _, position ->
  120. ringBleData?.setCheckupType(if (position == 0) ElinkCheckupType.COMPLEX else ElinkCheckupType.FAST)
  121. }
  122. }
  123. initButton(R.id.btn_health_ring_sensor_ota) {
  124. showListDialog(getString(R.string.select_sensor_ota_file), SENSOR_OTA_FILES) { item, _ ->
  125. lifecycleScope.launch {
  126. val fileData = readBytesFromAssets(item)
  127. ringBleData?.startSensorOTA(fileData)
  128. }
  129. }
  130. }
  131. initButton(R.id.btn_health_ring_ble_ota) {
  132. showListDialog(getString(R.string.select_ble_ota_file), BLE_OTA_FILES) { item, _ ->
  133. lifecycleScope.launch {
  134. val file = copyAssetToCache(item)
  135. file?.let {
  136. ringBleData?.startBleOTA(it.path, object : OnBleOTAListener {
  137. override fun onOtaSuccess() {
  138. addLog("${getString(R.string.ble_ota)} onOtaSuccess")
  139. }
  140. override fun onOtaFailure(cmd: Int, err: String?) {
  141. addLog("${getString(R.string.ble_ota)} onOtaFailure: $cmd, $err")
  142. }
  143. override fun onOtaProgress(
  144. progress: Float,
  145. currentCount: Int,
  146. maxCount: Int,
  147. ) {
  148. addLog("${getString(R.string.ble_ota)} onOtaProgress: ${progress.toInt()} %, $currentCount, $maxCount")
  149. }
  150. override fun onOtaStatus(status: Int) {
  151. addLog("${getString(R.string.ble_ota)} onOtaStatus: $status")
  152. }
  153. override fun onReconnect(mac: String?) {
  154. addLog("${getString(R.string.ble_ota)} onReconnect: $mac")
  155. }
  156. })
  157. }
  158. }
  159. }
  160. }
  161. initButton(R.id.btn_health_ring_get_history) {
  162. ringBleData?.getHistory()
  163. }
  164. initButton(R.id.btn_health_ring_get_next_history) {
  165. ringBleData?.getNextHistory()
  166. }
  167. initButton(R.id.btn_health_ring_get_history_over) {
  168. ringBleData?.getHistoryOver()
  169. }
  170. initButton(R.id.btn_health_ring_delete_history) {
  171. ringBleData?.deleteHistory()
  172. }
  173. initButton(R.id.btn_health_ring_start_checkup) {
  174. ringBleData?.startCheckup()
  175. }
  176. initButton(R.id.btn_health_ring_stop_checkup) {
  177. ringBleData?.stopCheckup()
  178. }
  179. initButton(R.id.btn_health_ring_sync_unix_time) {
  180. ringBleData?.syncUnixTime()
  181. }
  182. initButton(R.id.btn_health_ring_clear_log) {
  183. logList.clear()
  184. logAdapter.notifyDataSetChanged()
  185. }
  186. rvBleLog = findViewById<RecyclerView>(R.id.rv_ble_log).apply {
  187. layoutManager = LinearLayoutManager(this@ElinkHealthRingActivity)
  188. adapter = logAdapter
  189. }
  190. }
  191. override fun onOptionsItemSelected(item: MenuItem): Boolean {
  192. if (item.itemId == android.R.id.home) {
  193. finish()
  194. }
  195. return super.onOptionsItemSelected(item)
  196. }
  197. private fun initButton(@IdRes id: Int, onClick: () -> Unit) {
  198. findViewById<Button>(id).setOnClickListener {
  199. onClick()
  200. }
  201. }
  202. private fun connect() {
  203. bleData?.let {
  204. aiLinkBleManager?.connectDevice(it)
  205. }
  206. }
  207. override fun onPermissionOk() {
  208. }
  209. override fun onBindServiceSuccess() {
  210. aiLinkBleManager?.setOnCallbackBle(this)
  211. connect()
  212. }
  213. override fun onBindServiceFailed() {
  214. aiLinkBleManager?.setOnCallbackBle(null)
  215. }
  216. override fun onBmVersion(version: String) {
  217. addLog("${getString(R.string.ble_firmware_version)}: $version")
  218. }
  219. override fun bleOpen() {
  220. addLog(getString(R.string.ble_open))
  221. }
  222. override fun bleClose() {
  223. aiLinkBleManager?.disconnectAll()
  224. addLog(getString(R.string.ble_close))
  225. }
  226. override fun onConnecting(mac: String?) {
  227. addLog("${getString(R.string.connecting)}(${mac})")
  228. }
  229. override fun onConnectionSuccess(mac: String?) {
  230. addLog("${getString(R.string.connect_success)}(${mac})")
  231. }
  232. override fun onDisConnected(mac: String?, code: Int) {
  233. addLog("${getString(R.string.disconnect)}(${mac}, ${code})")
  234. }
  235. override fun onServicesDiscovered(mac: String?) {
  236. addLog("${getString(R.string.services_discovered)}(${mac})")
  237. bleDevice?.let {
  238. it.setOnBleVersionListener(this)
  239. ringBleData = ElinkHealthRingBleData(it)
  240. ringBleData?.setImplHealthRingResult(this)
  241. ringBleData?.setImplSensorOTA(object : ImplSensorOTA {
  242. override fun onFailure(type: ElinkSensorOTAErrorType) {
  243. addLog("${getString(R.string.sensor_ota)} onFailure: $type")
  244. }
  245. override fun onSuccess() {
  246. addLog("${getString(R.string.sensor_ota)} onSuccess")
  247. ringBleData?.endSensorOTA()
  248. }
  249. override fun onProgress(progress: Int) {
  250. addLog("${getString(R.string.sensor_ota)} onProgress: ${progress}%")
  251. }
  252. })
  253. }
  254. }
  255. private fun addLog(log: String) {
  256. val time = System.currentTimeMillis()
  257. val dateFormat = SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS", Locale.getDefault())
  258. logList.add("${dateFormat.format(Date(time))}: $log")
  259. logAdapter.notifyItemInserted(logList.size - 1)
  260. rvBleLog?.smoothScrollToPosition(logAdapter.itemCount - 1)
  261. }
  262. override fun onDestroy() {
  263. super.onDestroy()
  264. ringBleData?.setImplHealthRingResult(null)
  265. ringBleData?.setImplSensorOTA(null)
  266. aiLinkBleManager?.setOnCallbackBle(null)
  267. bleDevice?.setOnBleVersionListener(null)
  268. aiLinkBleManager?.disconnect(mac)
  269. }
  270. override fun startCheckup(success: Boolean) {
  271. addLog("${getString(R.string.start_checkup)}: $success")
  272. }
  273. override fun stopCheckup(success: Boolean) {
  274. addLog("${getString(R.string.stop_checkup)}: $success")
  275. }
  276. override fun onGetRealtimeData(data: ElinkCheckupRealtimeData) {
  277. addLog("${getString(R.string.checkup_realtime_data)}: $data")
  278. }
  279. override fun onGetCheckupPackets(data: ByteArray) {
  280. addLog("${getString(R.string.checkup_packets)}: ${data.toHexString()}")
  281. }
  282. override fun onGetCheckupDuration(duration: Int) {
  283. addLog("${getString(R.string.checkup_duration)}: ${duration}${getString(R.string.time_minutes)}")
  284. }
  285. override fun onGetHistory(histories: List<ElinkRingHistoryData>, total: Int, sentCount: Int) {
  286. addLog("${getString(R.string.checkup_history_data)}: $histories, $total, $sentCount, ${sentCount < total}")
  287. }
  288. override fun onGetDeviceStatus(status: ElinkRingDeviceStatus) {
  289. addLog("${getString(R.string.device_status)}: $status")
  290. }
  291. override fun onGetSensorVersion(version: String) {
  292. addLog("${getString(R.string.sensor_version)}: $version")
  293. }
  294. override fun onGetAutoCheckupStatus(open: Boolean) {
  295. addLog("${getString(R.string.auto_checkup_state)}: $open")
  296. }
  297. override fun onGetCheckupType(type: ElinkCheckupType) {
  298. addLog("${getString(R.string.checkup_type)}: $type")
  299. }
  300. override fun onNotifyHistoryGenerated() {
  301. addLog(getString(R.string.history_generated))
  302. }
  303. override fun onSetUnixTimeResult(success: Boolean) {
  304. addLog("${getString(R.string.sync_time)}: $success")
  305. }
  306. private fun showListDialog(
  307. title: String,
  308. items: Array<String>,
  309. callback: (String, Int) -> Unit,
  310. ) {
  311. AlertDialog.Builder(this)
  312. .setTitle(title)
  313. .setItems(items) { dialog, which ->
  314. val selectedItem = items[which]
  315. callback.invoke(selectedItem, which)
  316. dialog.dismiss()
  317. }.show()
  318. }
  319. private suspend fun copyAssetToCache(assetFileName: String): File? =
  320. withContext(Dispatchers.IO) {
  321. val outputFile = File(cacheDir, assetFileName)
  322. try {
  323. assets.open(assetFileName).use { inputStream ->
  324. FileOutputStream(outputFile).use { outputStream ->
  325. val buffer = ByteArray(4 * 1024) // 4 KB buffer size
  326. var bytesRead: Int
  327. while (inputStream.read(buffer).also { bytesRead = it } != -1) {
  328. outputStream.write(buffer, 0, bytesRead)
  329. }
  330. outputStream.flush()
  331. }
  332. }
  333. return@withContext outputFile
  334. } catch (e: Exception) {
  335. e.printStackTrace()
  336. return@withContext null
  337. }
  338. }
  339. private suspend fun readBytesFromAssets(fileName: String): ByteArray = withContext(Dispatchers.IO) {
  340. val inputStream: InputStream = assets.open(fileName)
  341. val buffer = ByteArrayOutputStream()
  342. val data = ByteArray(1024)
  343. var bytesRead: Int
  344. while (inputStream.read(data, 0, data.size).also { bytesRead = it } != -1) {
  345. buffer.write(data, 0, bytesRead)
  346. }
  347. buffer.close()
  348. inputStream.close()
  349. return@withContext buffer.toByteArray()
  350. }
  351. }