Skip to content

Commit 3a6f79f

Browse files
committed
纯蓝牙 LLSync APP 需求---添加保存localPsk逻辑,扫描设备显示产品名称和设备名称,优化蓝牙扫描,连续写操作间添加延时。设备属性上报,设备远程控制,获取设备最新信息,设备信息上报,设备 OTA 调试,完善OTA页面。
http://tapd.oa.com/NEW_IOT/prong/stories/view/1020393192863935819 Change-Id: I026a787fb146f43191cd90a570600c826006d0ef
1 parent 4ff3cd0 commit 3a6f79f

32 files changed

+1851
-251
lines changed

app/src/main/AndroidManifest.xml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,9 @@
4949
android:theme="@style/AppTheme"
5050
android:usesCleartextTraffic="true"
5151
tools:replace="android:allowBackup">
52+
<activity
53+
android:name=".kitlink.activity.BleOTADownloadActivity"
54+
android:exported="false" />
5255
<activity android:name=".kitlink.activity.PrivicyDialogActivity">
5356
<intent-filter>
5457
<action android:name="android.intent.action.MAIN" />
Lines changed: 326 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,326 @@
1+
package com.tencent.iot.explorer.link.kitlink.activity
2+
3+
import android.os.Handler
4+
import android.os.Message
5+
import android.text.TextUtils
6+
import android.view.View
7+
import com.alibaba.fastjson.JSONObject
8+
import com.tencent.iot.explorer.link.R
9+
import com.tencent.iot.explorer.link.core.auth.IoTAuth
10+
import com.tencent.iot.explorer.link.core.auth.callback.MyCallback
11+
import com.tencent.iot.explorer.link.core.auth.entity.DeviceEntity
12+
import com.tencent.iot.explorer.link.core.auth.response.BaseResponse
13+
import com.tencent.iot.explorer.link.core.link.entity.*
14+
import com.tencent.iot.explorer.link.core.link.exception.TCLinkException
15+
import com.tencent.iot.explorer.link.core.link.listener.BleDeviceConnectionListener
16+
import com.tencent.iot.explorer.link.core.link.service.BleConfigService
17+
import com.tencent.iot.explorer.link.core.log.L
18+
import com.tencent.iot.explorer.link.mvp.IPresenter
19+
import com.tencent.iot.explorer.link.retrofit.DownloadRequest
20+
import com.tencent.iot.explorer.link.retrofit.DownloadRequest.OnDownloadListener
21+
import kotlinx.android.synthetic.main.activity_ble_otadownload.*
22+
import kotlinx.android.synthetic.main.menu_back_layout.*
23+
import kotlinx.coroutines.*
24+
import java.io.*
25+
26+
27+
class BleOTADownloadActivity : PActivity(), CoroutineScope by MainScope() {
28+
val MSG_DOWNLOAD_REFRESH = 0
29+
val MSG_DOWNLOAD_SUCCESS = 1
30+
val MSG_DOWNLOAD_FAILED = 2
31+
val MSG_UPLOAD_REFRESH = 3
32+
val MSG_UPLOAD_SUCCESS = 4
33+
val MSG_UPLOAD_FAILED = 5
34+
35+
private var TAG = BleOTADownloadActivity::class.java.simpleName
36+
private var deviceEntity: DeviceEntity? = null
37+
private var connectBleJob: Job? = null
38+
39+
private var targetVersion: String? = null // 要升级到的ota版本
40+
@Volatile
41+
private var downloadprogress: Int = 0// 下载进度值
42+
private var otaFilePath: String? = null // 下载固件时本地的存储路径
43+
44+
private var mtusize = 0
45+
@Volatile
46+
private var uploadprogress: Int = 0// 写入进度值
47+
48+
override fun getPresenter(): IPresenter? {
49+
return null
50+
}
51+
52+
override fun onResume() {
53+
super.onResume()
54+
BleConfigService.get().connetionListener = bleDeviceConnectionListener
55+
}
56+
57+
override fun getContentView(): Int {
58+
return R.layout.activity_ble_otadownload
59+
}
60+
61+
override fun initView() {
62+
tv_title.text = getString(R.string.firmware_update)
63+
stopScanBleDev(true)
64+
deviceEntity = get("device")
65+
mtusize = get("mtusize") ?: 0
66+
downloadDeviceOTAInfo()
67+
}
68+
69+
override fun setListener() {
70+
iv_back.setOnClickListener { finish() }
71+
tv_finish.setOnClickListener { finish() }
72+
}
73+
74+
75+
private fun startScanBleDev() {
76+
ble_connect_layout.visibility = View.VISIBLE
77+
BleConfigService.get().startScanBluetoothDevices()
78+
search_ble_dev_layout.visibility = View.VISIBLE
79+
search_reault_layout.visibility = View.GONE
80+
}
81+
82+
private fun stopScanBleDev(connected: Boolean) {
83+
launch(Dispatchers.Main) {
84+
ble_connect_layout.visibility = View.VISIBLE
85+
BleConfigService.get().stopScanBluetoothDevices()
86+
search_ble_dev_layout.visibility = View.GONE
87+
search_reault_layout.visibility = View.VISIBLE
88+
if (connected) {
89+
search_reault_layout.setBackgroundResource(R.color.blue_006EFF)
90+
retry_connect.setTextColor(this@BleOTADownloadActivity.resources.getColor(R.color.white))
91+
retry_connect.setText(R.string.break_ble_connect)
92+
retry_connect.setOnClickListener {
93+
BleConfigService.get().bluetoothGatt?.let {
94+
it?.close()
95+
stopScanBleDev(false)
96+
}
97+
}
98+
} else {
99+
search_reault_layout.setBackgroundResource(R.color.red_E65A59)
100+
retry_connect.setTextColor(this@BleOTADownloadActivity.resources.getColor(R.color.white))
101+
retry_connect.setText(R.string.scanning_retry)
102+
retry_connect.setOnClickListener { startScanBleDev() }
103+
}
104+
}
105+
}
106+
107+
private var bleDeviceConnectionListener = object: BleDeviceConnectionListener {
108+
override fun onBleDeviceFounded(bleDevice: BleDevice) {
109+
if (bleDevice.productId == deviceEntity?.ProductId && !TextUtils.isEmpty(bleDevice.productId)) {
110+
//&& bleDevice.devName == deviceEntity?.DeviceName) {
111+
BleConfigService.get().bluetoothGatt = BleConfigService.get().connectBleDeviceAndGetLocalPsk(bleDevice, deviceEntity?.ProductId, deviceEntity?.DeviceName)
112+
} else if (!TextUtils.isEmpty(bleDevice.bindTag)) {
113+
deviceEntity?.let {
114+
if (bleDevice.bindTag == BleConfigService.bytesToHex(BleConfigService.getBindTag(it.ProductId, it.DeviceName))) {
115+
BleConfigService.get().bluetoothGatt = BleConfigService.get().connectBleDeviceAndGetLocalPsk(bleDevice, deviceEntity?.ProductId, deviceEntity?.DeviceName)
116+
}
117+
}
118+
}
119+
}
120+
121+
override fun onBleDeviceConnected() {
122+
launch {
123+
BleConfigService.get().bluetoothGatt?.let {
124+
delay(3000)
125+
// if (BleConfigService.get().setMtuSize(it, 512)) return@launch
126+
launch (Dispatchers.Main) {
127+
delay(1000)
128+
BleConfigService.get().bluetoothGatt?.let {
129+
BleConfigService.get().stopScanBluetoothDevices()
130+
if (!BleConfigService.get().connectSubDevice(it)) {
131+
stopScanBleDev(false)
132+
return@launch
133+
} else {
134+
135+
}
136+
}
137+
}
138+
}
139+
}
140+
}
141+
override fun onBleDeviceDisconnected(exception: TCLinkException) {
142+
stopScanBleDev(false)
143+
launch (Dispatchers.Main) {
144+
startScanBleDev()
145+
}
146+
}
147+
override fun onBleDeviceInfo(bleDeviceInfo: BleDeviceInfo) {}
148+
override fun onBleSetWifiModeResult(success: Boolean) {}
149+
override fun onBleSendWifiInfoResult(success: Boolean) {}
150+
override fun onBleWifiConnectedInfo(wifiConnectInfo: BleWifiConnectInfo) {}
151+
override fun onBlePushTokenResult(success: Boolean) {}
152+
override fun onMtuChanged(mtu: Int, status: Int) {
153+
L.d(TAG, "onMtuChanged mtu $mtu status $status")
154+
}
155+
override fun onBleBindSignInfo(bleDevBindCondition: BleDevBindCondition) {}
156+
override fun onBleSendSignInfo(bleDevSignResult: BleDevSignResult) {
157+
stopScanBleDev(true)
158+
connectBleJob?.cancel()
159+
}
160+
override fun onBleUnbindSignInfo(signInfo: String) {}
161+
override fun onBlePropertyValue(bleDeviceProperty: BleDeviceProperty) {}
162+
override fun onBleControlPropertyResult(result: Int) {}
163+
override fun onBleRequestCurrentProperty() {}
164+
override fun onBleNeedPushProperty(eventId: Int, bleDeviceProperty: BleDeviceProperty) {}
165+
override fun onBleReportActionResult(reason: Int, actionId: Int, bleDeviceProperty: BleDeviceProperty) {}
166+
override fun onBleDeviceFirmwareVersion(firmwareVersion: BleDeviceFirmwareVersion) {
167+
if (firmwareVersion.mtuFlag == 1) { // 是否设置 mtu 当 mtu flag为 1 时,进行 MTU 设置;当 mtu flag 为 0 时,不设置 MTU
168+
BleConfigService.get().setMtuSize(BleConfigService.get().bluetoothGatt, firmwareVersion.mtuSize)
169+
}
170+
if (targetVersion.equals(firmwareVersion.version)) { //ble设备重启后,firmware版本和目标版本一致,判定ota升级成功
171+
launch(Dispatchers.Main) {
172+
L.d(TAG, "ota升级成功~")
173+
rl_ota_progress.visibility = View.GONE
174+
rl_ota_success.visibility = View.VISIBLE
175+
tv_finish.visibility = View.VISIBLE
176+
tv_upgrade_success_version.text = getString(R.string.firmware_update_success_version, targetVersion)
177+
}
178+
}
179+
}
180+
181+
override fun onBleDevOtaUpdateResponse(otaUpdateResponse: BleDevOtaUpdateResponse) {
182+
L.d(TAG, "onBleDevOtaUpdateResponse")
183+
if (otaUpdateResponse.allowUpdate) {
184+
otaFilePath?.let {
185+
BleConfigService.get().sendOtaFirmwareData(BleConfigService.get().bluetoothGatt,
186+
it, otaUpdateResponse)
187+
}
188+
}
189+
}
190+
191+
override fun onBleDevOtaUpdateResult(success: Boolean, errorCode: Int) {
192+
if (success) {
193+
handler.sendEmptyMessage(MSG_UPLOAD_SUCCESS)
194+
} else {
195+
handler.sendEmptyMessage(MSG_UPLOAD_FAILED)
196+
}
197+
}
198+
199+
override fun onBleDevOtaReceivedProgressResponse(progress: Int) {
200+
L.d(TAG,"progress:${progress}")
201+
uploadprogress = progress
202+
refreshProgress(false)
203+
reportProgress2Cloud(progress, "updating")
204+
}
205+
206+
override fun onBleDeviceMtuSize(size: Int) {}
207+
override fun onBleDeviceTimeOut(timeLong: Int) {}
208+
}
209+
210+
// 上报设备OTA状态进度 (下载、更新升级、烧录) State: downloading updating burning
211+
private fun reportProgress2Cloud(progress: Int, state: String) {
212+
targetVersion?.let {
213+
IoTAuth.deviceImpl.reportOTAStatus("${deviceEntity?.ProductId}/${deviceEntity?.DeviceName}", state,
214+
it, progress, object: MyCallback {
215+
override fun fail(msg: String?, reqCode: Int) {}
216+
217+
override fun success(response: BaseResponse, reqCode: Int) {}
218+
})
219+
}
220+
}
221+
222+
private fun downloadDeviceOTAInfo() {
223+
IoTAuth.deviceImpl.getDeviceOTAInfo("${deviceEntity?.ProductId}/${deviceEntity?.DeviceName}", object: MyCallback {
224+
override fun fail(msg: String?, reqCode: Int) {
225+
226+
}
227+
228+
override fun success(response: BaseResponse, reqCode: Int) {
229+
val json = response.data as JSONObject
230+
val firmwareURL = json.getString("FirmwareURL")
231+
if (firmwareURL != null) {
232+
targetVersion = json.getString("TargetVersion")
233+
// val uploadVersion = json.getString("UploadVersion")
234+
if (TextUtils.isEmpty(otaFilePath)) {
235+
otaFilePath = cacheDir.absolutePath + "/${deviceEntity?.ProductId}_${deviceEntity?.DeviceName}_${targetVersion}"
236+
}
237+
downloadOtaFirmware(firmwareURL)
238+
}
239+
}
240+
})
241+
}
242+
243+
private fun downloadOtaFirmware(url: String) {
244+
DownloadRequest.get().download(url, otaFilePath, downloadlistener)
245+
}
246+
247+
248+
var downloadlistener: OnDownloadListener = object : OnDownloadListener {
249+
override fun onDownloadSuccess(requestId: String) {
250+
handler.sendEmptyMessage(MSG_DOWNLOAD_SUCCESS)
251+
}
252+
253+
override fun onDownloading(requestId: String, progress: Int) {
254+
downloadprogress = progress
255+
refreshProgress(true)
256+
reportProgress2Cloud(progress, "downloading")
257+
}
258+
259+
override fun onDownloadFailed(requestId: String) {
260+
handler.sendEmptyMessage(MSG_DOWNLOAD_FAILED)
261+
}
262+
}
263+
264+
265+
var handler: Handler = object : Handler() {
266+
override fun handleMessage(msg: Message) {
267+
super.handleMessage(msg)
268+
when(msg.what) {
269+
MSG_DOWNLOAD_REFRESH -> {
270+
if (progress_download != null) {
271+
progress_download.progress = downloadprogress.toFloat()
272+
tv_upgradeing.text = getString(R.string.firmware_upgrading_tip, downloadprogress) + "%"
273+
}
274+
if (downloadprogress < 100) {
275+
L.d(TAG, "downloadprogress:${downloadprogress}, progressBar.max:${progress_download?.max}")
276+
// onDismisListener.onDownloadProgress(count, progressBar.max as Int)
277+
}
278+
}
279+
MSG_DOWNLOAD_SUCCESS -> {
280+
progress_download?.progress = 100f
281+
L.d(TAG, "onDownloadSuccess:${otaFilePath}")
282+
reportProgress2Cloud(100, "downloading")
283+
otaFilePath?.let {
284+
targetVersion?.let { it1 ->
285+
BleConfigService.get().requestOTAInfo(BleConfigService.get().bluetoothGatt,
286+
it, it1
287+
)
288+
}
289+
}
290+
}
291+
MSG_DOWNLOAD_FAILED -> {
292+
L.d(TAG, "onDownloadFailed:${otaFilePath}")
293+
}
294+
MSG_UPLOAD_REFRESH -> {
295+
if (progress_download != null) {
296+
progress_download.progress = uploadprogress.toFloat()
297+
tv_upgradeing.text = getString(R.string.firmware_writing, uploadprogress) + "%"
298+
}
299+
if (downloadprogress < 100) {
300+
L.d(TAG, "uploadprogress:${uploadprogress}, progressBar.max:${progress_download?.max}")
301+
}
302+
}
303+
MSG_UPLOAD_SUCCESS -> {
304+
progress_download?.progress = 100f
305+
L.d(TAG, "onUploadSuccess:${otaFilePath}")
306+
reportProgress2Cloud(100, "updating")
307+
}
308+
MSG_UPLOAD_FAILED -> {
309+
L.d(TAG, "onUpFailed:${otaFilePath}")
310+
}
311+
}
312+
}
313+
}
314+
315+
private fun refreshProgress(download: Boolean) { //通过url下载固件文件,还是写入固件给ble设备
316+
// 进度在 100 以内允许刷新
317+
if (downloadprogress <= 100) {
318+
if (download) {
319+
handler.sendEmptyMessage(MSG_DOWNLOAD_REFRESH)
320+
} else {
321+
handler.sendEmptyMessage(MSG_UPLOAD_REFRESH)
322+
}
323+
}
324+
}
325+
326+
}

0 commit comments

Comments
 (0)