扫一扫

This commit is contained in:
fengjignqi
2026-09-20 09:18:26 +08:00
parent 981cfae71c
commit 3f42536710
25 changed files with 1258 additions and 96 deletions

View File

@@ -0,0 +1,22 @@
package com.juhabi.birds.device
import com.chad.library.adapter.base.BaseQuickAdapter
import com.chad.library.adapter.base.viewholder.BaseViewHolder
import com.juhabi.birds.R
import com.juhabi.birds.device.entity.DeviceEntity
/** 临时设备列表适配器,后续可根据设计稿替换 item 展示。 */
class DeviceAdapter : BaseQuickAdapter<DeviceEntity, BaseViewHolder>(R.layout.device_item) {
init {
addChildClickViewIds(R.id.img_tips)
}
override fun convert(holder: BaseViewHolder, item: DeviceEntity) {
holder.setText(R.id.tv_device_name, item.name.ifBlank { "未命名设备" })
holder.setText(R.id.tv_device_status, item.signalText.ifBlank { "离线" })
holder.setText(R.id.tv_device_no, "设备编号:${item.deviceNo}")
holder.setText(R.id.tv_device_imei, "IMEI:${item.imei}")
holder.setText(R.id.tv_device_detail, "型号:${item.model ?: "-"} 信号:${item.signalLevel} 头部电量:${item.hoodBattery ?: "-"} 戒指电量:${item.ringBattery ?: "-"}")
holder.setText(R.id.tv_device_location, "连接:${item.hoodConnectedText} 地址:${item.address ?: "暂无地址"}")
}
}

View File

@@ -3,8 +3,11 @@ package com.juhabi.birds.device
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import com.gyf.immersionbar.ImmersionBar
import com.juhabi.birds.R
import com.juhabi.birds.base.BaseActivity
import com.juhabi.birds.databinding.BindDeviceLayoutBinding
import com.juhabi.birds.databinding.DeviceDetailLayoutBinding
import com.juhabi.birds.device.entity.DeviceEntity
import com.juhabi.birds.device.mvp.DeviceContract
import com.juhabi.birds.device.mvp.DeviceImpl
@@ -13,28 +16,100 @@ import com.juhabi.birds.device.mvp.DevicePresenter
/**
* 设备详情
*/
class DeviceDetailActivity: BaseActivity<BindDeviceLayoutBinding, DevicePresenter>() , DeviceContract.DeviceView,
class DeviceDetailActivity :
BaseActivity<DeviceDetailLayoutBinding, DevicePresenter>(), DeviceContract.DeviceView,
View.OnClickListener {
override fun createBinding(inflater: LayoutInflater): BindDeviceLayoutBinding {
return BindDeviceLayoutBinding.inflate(inflater)
companion object {
const val EXTRA_DEVICE = "extra_device"
const val HOOD_COVER_ON = "0"
const val HOOD_COVER_OFF = "1"
}
private var device: DeviceEntity? = null
override fun createBinding(inflater: LayoutInflater): DeviceDetailLayoutBinding {
return DeviceDetailLayoutBinding.inflate(inflater)
}
override fun createPresenter(): DevicePresenter? {
return DevicePresenter(DeviceImpl())
}
override fun initView(savedInstanceState: Bundle?) {
override fun initWindows() {
ImmersionBar.with(this)
.statusBarView(binding.statusBarView)
.statusBarDarkFont(true)
.navigationBarColor(R.color.color_zhu)
.navigationBarDarkIcon(true)
.init()
}
override fun initView(savedInstanceState: Bundle?) {
device = intent.getSerializableExtra(EXTRA_DEVICE) as? DeviceEntity
binding.includedTitle.tvTitle.text = device?.name ?: "设备详情"
val deviceNo = device?.deviceNo
if (deviceNo.isNullOrBlank()) {
showError("设备信息无效")
} else {
presenter?.getDeviceDetail(deviceNo)
}
listOf(
binding.tvBind,
binding.switchCtrl,
binding.includedTitle.imgBack,
).forEach { button ->
button.setOnClickListener(this)
}
}
override fun onClick(view: View) {
when(view.id){
R.id.img_back ->{
finish()
}
R.id.switch_ctrl ->{
if(device?.status == "0"){
showMessage("设备已经离线")
return;
}
val currentDevice = device ?: return
// hoodCoverOn:0 表示打开,1 表示关闭;点击后发送切换后的目标状态。
val coverOn = currentDevice.hoodCoverOn != HOOD_COVER_ON
presenter?.controlEye(currentDevice.deviceNo, coverOn)
}
}
}
override fun showError(message: String) {
}
override fun getBindDeviceListSuccess(devices: List<DeviceEntity>) {
}
override fun getDeviceDetailSuccess(device: DeviceEntity) {
this.device = device
binding.includedTitle.tvTitle.text = device.name
binding.tvBind.text = "设备编号:${device.deviceNo}"
binding.switchCtrl.isChecked = device.hoodCoverOn == HOOD_COVER_ON
}
override fun controlEyeSuccess(message: String) {
showMessage(message)
device = device?.copy(
hoodCoverOn = if (binding.switchCtrl.isChecked) {
HOOD_COVER_ON
} else {
HOOD_COVER_OFF
}
)
}
override fun bindDeviceSuccess(message: String) {
}
override fun onClick(view: View) {
}
}
}

View File

@@ -1,11 +1,13 @@
package com.juhabi.birds.device
import android.Manifest
import android.content.Intent
import android.os.Build
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import com.juhabi.birds.R
import com.juhabi.birds.base.BaseActivity
import com.juhabi.birds.base.BaseFragment
import com.juhabi.birds.custom.DeviceActionPopupWindow
import com.juhabi.birds.custom.CustomRefreshHeader
@@ -14,12 +16,12 @@ import com.juhabi.birds.device.entity.DeviceEntity
import com.juhabi.birds.device.mvp.DeviceContract
import com.juhabi.birds.device.mvp.DeviceImpl
import com.juhabi.birds.device.mvp.DevicePresenter
import com.juhabi.birds.main.fragment.home.mvp.HomeContract
import com.juhabi.birds.main.fragment.home.mvp.HomeImpl
import com.juhabi.birds.main.fragment.home.mvp.HomePresenter
import com.juhabi.birds.util.ToastUtil
import com.maning.mlkitscanner.scan.MNScanManager
import com.maning.mlkitscanner.scan.callback.act.MNScanCallback
import com.scwang.smart.refresh.footer.ClassicsFooter
class IndexFragment : BaseFragment<FragmentHomeBinding, DevicePresenter>(),
class DeviceFragment : BaseFragment<FragmentHomeBinding, DevicePresenter>(),
DeviceContract.DeviceView, View.OnClickListener {
private val deviceAdapter = DeviceAdapter()
@@ -37,10 +39,17 @@ class IndexFragment : BaseFragment<FragmentHomeBinding, DevicePresenter>(),
override fun initView(savedInstanceState: Bundle?) {
listOf(
binding.tvIndex,
binding.imgScan,
).forEach { button ->
button.setOnClickListener(this)
}
binding.rvDevices.adapter = deviceAdapter
deviceAdapter.setOnItemClickListener { _, _, position ->
val device = deviceAdapter.data[position]
goTo(DeviceDetailActivity::class.java, Bundle().apply {
putSerializable(DeviceDetailActivity.EXTRA_DEVICE, device)
})
}
deviceAdapter.setOnItemChildClickListener { adapter, view, position ->
when (view.id) {
R.id.img_tips -> {
@@ -102,6 +111,17 @@ class IndexFragment : BaseFragment<FragmentHomeBinding, DevicePresenter>(),
R.id.tv_index ->{
goTo(BindDeviceActivity::class.java,null)
}
//扫一扫:先申请相机和相册权限,授权通过后再拉起扫码页。
R.id.img_scan -> {
getMorePermissionRequst(
scanPermissions(),
"相机和相册权限",
"扫一扫功能需要使用相机及访问相册权限",
REQUEST_CAMERA_AND_ALBUM
)
}
}
}
@@ -168,17 +188,67 @@ class IndexFragment : BaseFragment<FragmentHomeBinding, DevicePresenter>(),
super.showError(message)
}
/** 接口暂无数据时提供临时设备,方便先验收列表布局。 */
private fun temporaryDevices(): List<DeviceEntity> = listOf(
DeviceEntity("暂无地址", "BM202609160005", null, "0", "已断开", "860000000000005", null, null, null, "BM202609160005", null, 0, "离线", "1"),
DeviceEntity("暂无地址", "BM202609160006", null, "0", "已断开", "860000000000006", null, null, null, "BM202609160006", null, 0, "离线", "1"),
DeviceEntity("暂无地址", "BM202609160007", null, "0", "已断开", "860000000000007", null, null, null, "BM202609160007", null, 0, "离线", "1")
)
private companion object {
const val FIRST_PAGE = 1
const val PAGE_SIZE = 10
const val REMOVE_ANIMATION_DELAY_MS = 300L
const val REQUEST_CAMERA_AND_ALBUM = 1001
}
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<out String>,
grantResults: IntArray
) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
}
/** 相机和相册均已授权后,自动打开扫一扫。 */
override fun onPermissionsGranted(permissions: Array<String>) {
startDeviceScan()
}
override fun onPermissionsDenied(permissions: Array<String>, permanentlyDenied: Boolean) {
super.onPermissionsDenied(permissions, permanentlyDenied)
}
/** Android 13 及以上申请读图权限,更低版本申请存储读取权限。 */
private fun scanPermissions(): Array<String> {
val albumPermission = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
Manifest.permission.READ_MEDIA_IMAGES
} else {
Manifest.permission.READ_EXTERNAL_STORAGE
}
return arrayOf(Manifest.permission.CAMERA, albumPermission)
}
/** 使用 MNMLKitScanner 默认扫码页识别二维码或条形码。 */
private fun startDeviceScan() {
MNScanManager.startScan(mContext, object : MNScanCallback {
override fun onActivityResult(resultCode: Int, data: Intent) {
when (resultCode) {
MNScanManager.RESULT_SUCCESS -> {
val results = data.getStringArrayListExtra(MNScanManager.INTENT_KEY_RESULT_SUCCESS)
val content = results?.filter { it.isNotBlank() }?.joinToString("\n").orEmpty()
if (content.isBlank()) {
ToastUtil.showShort(mContext, "未识别到内容")
} else {
ToastUtil.showShort(mContext, content)
}
}
MNScanManager.RESULT_FAIL -> {
val resultError = data.getStringExtra(MNScanManager.INTENT_KEY_RESULT_ERROR)
ToastUtil.showShort(mContext, resultError.takeUnless { it.isNullOrBlank() } ?: "扫码失败")
}
MNScanManager.RESULT_CANCLE -> {
ToastUtil.showShort(mContext, getString(R.string.cancel_scan))
}
}
}
})
}
}

View File

@@ -16,5 +16,6 @@ data class DeviceEntity(
val ringBattery: Int?,
val signalLevel: Int,
val signalText: String,
val status: String
val status: String,
val hoodCoverOn: String
) : Serializable

View File

@@ -0,0 +1,55 @@
package com.juhabi.birds.device.mvp
import com.juhabi.birds.base.IView
import com.juhabi.birds.device.entity.DeviceEntity
import com.juhabi.birds.login.model.UserEntity
import com.juhabi.birds.login.mvp.LoginContract
import com.juhabi.birds.network.BaseBean
import org.json.JSONObject
interface DeviceContract {
interface Callback<T> {
fun success(data: T)
fun fail(message: String)
}
interface DataModel {
/** 查询已绑定的设备列表。 */
fun getBindDeviceList(params: Map<String, String>, callback: Callback<List<DeviceEntity>>)
fun getDeviceDetail(deviceNo: String, callback: Callback<DeviceEntity>)
/** 控制设备眼罩开关。 */
fun controlEye(deviceNo: String, coverOn: Boolean, callback: Callback<BaseBean<Any>>)
/** 根据 IMEI 和设备名称绑定设备。 */
fun bindDevice(json: JSONObject, callback: Callback<BaseBean<Any>>)
/** 根据设备编号修改设备名称。 */
fun renameDevice(
deviceNo: String,
json: JSONObject,
callback: Callback<BaseBean<Any>>
)
/** 根据设备编号解绑设备。 */
fun unbindDevice(deviceNo: String, callback: Callback<BaseBean<Any>>)
/** 取消设备模块中尚未完成的网络请求。 */
fun cancel()
}
interface DeviceView : IView {
fun getBindDeviceListSuccess(devices: List<DeviceEntity>)
fun getDeviceDetailSuccess(device: DeviceEntity) {}
fun controlEyeSuccess(message: String) {}
fun bindDeviceSuccess(message: String)
/** 设备重命名成功回调,不关心该事件的页面可以不实现。 */
fun renameDeviceSuccess(message: String) {
}
/** 设备解绑成功回调,不关心该事件的页面可以不实现。 */
fun unbindDeviceSuccess(message: String) {
}
}
}

View File

@@ -0,0 +1,276 @@
package com.juhabi.birds.device.mvp
import android.os.Handler
import android.os.Looper
import com.juhabi.birds.device.entity.DeviceEntity
import com.juhabi.birds.network.ApiClient
import com.juhabi.birds.network.BaseBean
import com.juhabi.birds.network.NetworkManager
import org.json.JSONObject
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response
class DeviceImpl : DeviceContract.DataModel {
private val mainHandler = Handler(Looper.getMainLooper())
private var deviceListCall: Call<BaseBean<List<DeviceEntity>>>? = null
private var deviceDetailCall: Call<BaseBean<DeviceEntity>>? = null
private var controlEyeCall: Call<BaseBean<Any>>? = null
private var bindDeviceCall: Call<BaseBean<Any>>? = null
private var renameDeviceCall: Call<BaseBean<Any>>? = null
private var unbindDeviceCall: Call<BaseBean<Any>>? = null
/** 请求当前账号已绑定的设备列表。 */
override fun getBindDeviceList(params: Map<String, String>, callback: DeviceContract.Callback<List<DeviceEntity>>) {
deviceListCall?.cancel()
deviceListCall = ApiClient.apiService
.getBindDeviceList(NetworkManager.getToken(), params)
.also { call ->
call.enqueue(object : Callback<BaseBean<List<DeviceEntity>>> {
override fun onResponse(
call: Call<BaseBean<List<DeviceEntity>>>,
response: Response<BaseBean<List<DeviceEntity>>>
) {
mainHandler.post {
val bean = response.body()
val devices = bean?.rows ?: bean?.data
if (
response.isSuccessful &&
(bean?.code == 0 || bean?.code == 200) &&
devices != null
) {
callback.success(devices)
} else {
callback.fail(
bean?.message?.ifBlank { null } ?: "获取设备列表失败"
)
}
}
}
override fun onFailure(
call: Call<BaseBean<List<DeviceEntity>>>,
error: Throwable
) {
if (!call.isCanceled) {
mainHandler.post {
callback.fail(error.message ?: "网络请求失败")
}
}
}
})
}
}
override fun getDeviceDetail(
deviceNo: String,
callback: DeviceContract.Callback<DeviceEntity>
) {
deviceDetailCall?.cancel()
deviceDetailCall = ApiClient.apiService
.getDeviceDetail(NetworkManager.getToken(), deviceNo)
.also { call ->
call.enqueue(object : Callback<BaseBean<DeviceEntity>> {
override fun onResponse(
call: Call<BaseBean<DeviceEntity>>,
response: Response<BaseBean<DeviceEntity>>
) {
mainHandler.post {
val bean = response.body()
val device = bean?.data
if (
response.isSuccessful &&
(bean?.code == 0 || bean?.code == 200) &&
device != null
) {
callback.success(device)
} else {
callback.fail(
bean?.message?.ifBlank { null } ?: "获取设备详情失败"
)
}
}
}
override fun onFailure(
call: Call<BaseBean<DeviceEntity>>,
error: Throwable
) {
if (!call.isCanceled) {
mainHandler.post {
callback.fail(error.message ?: "网络请求失败")
}
}
}
})
}
}
/** 请求服务端控制设备眼罩开关。 */
override fun controlEye(
deviceNo: String,
coverOn: Boolean,
callback: DeviceContract.Callback<BaseBean<Any>>
) {
controlEyeCall?.cancel()
val json = JSONObject().apply {
put("deviceNo", deviceNo)
put("coverOn", coverOn)
}
controlEyeCall = ApiClient.apiService
.controlEye(NetworkManager.getToken(), deviceNo, NetworkManager.toRequestBody(json))
.also { call ->
call.enqueue(object : Callback<BaseBean<Any>> {
override fun onResponse(
call: Call<BaseBean<Any>>,
response: Response<BaseBean<Any>>
) {
mainHandler.post {
val bean = response.body()
if (response.isSuccessful && (bean?.code == 0 || bean?.code == 200)) {
callback.success(bean)
} else {
callback.fail(bean?.message?.ifBlank { null } ?: "控制眼罩失败")
}
}
}
override fun onFailure(call: Call<BaseBean<Any>>, error: Throwable) {
if (!call.isCanceled) {
mainHandler.post {
callback.fail(error.message ?: "网络请求失败")
}
}
}
})
}
}
/** 将设备 IMEI 和名称提交到服务器完成绑定。 */
override fun bindDevice(
json: JSONObject,
callback: DeviceContract.Callback<BaseBean<Any>>
) {
bindDeviceCall?.cancel()
bindDeviceCall = ApiClient.apiService
.bindDevice(NetworkManager.getToken(), NetworkManager.toRequestBody(json))
.also { call ->
call.enqueue(object : Callback<BaseBean<Any>> {
override fun onResponse(
call: Call<BaseBean<Any>>,
response: Response<BaseBean<Any>>
) {
mainHandler.post {
val bean = response.body()
if (
response.isSuccessful &&
(bean?.code == 0 || bean?.code == 200)
) {
callback.success(bean)
} else {
callback.fail(
bean?.message?.ifBlank { null } ?: "绑定设备失败"
)
}
}
}
override fun onFailure(call: Call<BaseBean<Any>>, error: Throwable) {
if (!call.isCanceled) {
mainHandler.post {
callback.fail(error.message ?: "网络请求失败")
}
}
}
})
}
}
/** 将新的设备名称提交到服务器。 */
override fun renameDevice(
deviceNo: String,
json: JSONObject,
callback: DeviceContract.Callback<BaseBean<Any>>
) {
renameDeviceCall?.cancel()
renameDeviceCall = ApiClient.apiService
.deviceRename(deviceNo = deviceNo, headers = NetworkManager.getToken(), requestBody = NetworkManager.toRequestBody(json))
.also { call ->
call.enqueue(object : Callback<BaseBean<Any>> {
override fun onResponse(
call: Call<BaseBean<Any>>,
response: Response<BaseBean<Any>>
) {
mainHandler.post {
val bean = response.body()
if (response.isSuccessful && (bean?.code == 0 || bean?.code == 200)) {
callback.success(bean)
} else {
callback.fail(bean?.message?.ifBlank { null } ?: "设备重命名失败")
}
}
}
override fun onFailure(call: Call<BaseBean<Any>>, error: Throwable) {
if (!call.isCanceled) {
mainHandler.post {
callback.fail(error.message ?: "网络请求失败")
}
}
}
})
}
}
/** 按设备编号请求服务端解绑。 */
override fun unbindDevice(
deviceNo: String,
callback: DeviceContract.Callback<BaseBean<Any>>
) {
unbindDeviceCall?.cancel()
unbindDeviceCall = ApiClient.apiService
.unbindDevice(headers = NetworkManager.getToken(), deviceNo = deviceNo)
.also { call ->
call.enqueue(object : Callback<BaseBean<Any>> {
override fun onResponse(
call: Call<BaseBean<Any>>,
response: Response<BaseBean<Any>>
) {
mainHandler.post {
val bean = response.body()
if (response.isSuccessful && (bean?.code == 0 || bean?.code == 200)) {
callback.success(bean)
} else {
callback.fail(bean?.message?.ifBlank { null } ?: "设备解绑失败")
}
}
}
override fun onFailure(call: Call<BaseBean<Any>>, error: Throwable) {
if (!call.isCanceled) {
mainHandler.post {
callback.fail(error.message ?: "网络请求失败")
}
}
}
})
}
}
/** 取消所有设备请求并清理主线程回调。 */
override fun cancel() {
deviceListCall?.cancel()
deviceListCall = null
deviceDetailCall?.cancel()
deviceDetailCall = null
controlEyeCall?.cancel()
controlEyeCall = null
bindDeviceCall?.cancel()
bindDeviceCall = null
renameDeviceCall?.cancel()
renameDeviceCall = null
unbindDeviceCall?.cancel()
unbindDeviceCall = null
mainHandler.removeCallbacksAndMessages(null)
}
}

View File

@@ -0,0 +1,166 @@
package com.juhabi.birds.device.mvp
import com.juhabi.birds.base.BasePresenter
import com.juhabi.birds.device.entity.DeviceEntity
import com.juhabi.birds.network.BaseBean
import org.json.JSONObject
class DevicePresenter(
private val deviceImpl: DeviceImpl
) : BasePresenter<DeviceContract.DeviceView>() {
/** 获取当前账号已经绑定的设备列表。 */
fun getBindDeviceList(params: Map<String, String> = emptyMap()) {
val currentView = view ?: return
currentView.showLoading()
deviceImpl.getBindDeviceList(params, object : DeviceContract.Callback<List<DeviceEntity>> {
override fun success(data: List<DeviceEntity>) {
view?.apply {
hideLoading()
getBindDeviceListSuccess(data)
}
}
override fun fail(message: String) {
view?.apply {
hideLoading()
showError(message)
}
}
}
)
}
/**
* 获取设备详情
*/
fun getDeviceDetail(deviceNo: String) {
val currentView = view ?: return
currentView.showLoading()
deviceImpl.getDeviceDetail(
deviceNo,
object : DeviceContract.Callback<DeviceEntity> {
override fun success(data: DeviceEntity) {
view?.apply {
hideLoading()
getDeviceDetailSuccess(data)
}
}
override fun fail(message: String) {
view?.apply {
hideLoading()
showError(message)
}
}
}
)
}
/** 根据目标状态控制设备眼罩开关。 */
fun controlEye(deviceNo: String, coverOn: Boolean) {
val currentView = view ?: return
currentView.showLoading()
deviceImpl.controlEye(deviceNo, coverOn, object : DeviceContract.Callback<BaseBean<Any>> {
override fun success(data: BaseBean<Any>) {
view?.apply {
hideLoading()
controlEyeSuccess(data.message.ifBlank { "眼罩状态已更新" })
}
}
override fun fail(message: String) {
view?.apply {
hideLoading()
showError(message)
}
}
})
}
/** 使用设备 IMEI 和名称发起绑定请求。 */
fun bindDevice(imei: String, name: String) {
val currentView = view ?: return
currentView.showLoading()
val json = JSONObject().apply {
put("imei", imei)
put("name", name)
}
deviceImpl.bindDevice(json, object : DeviceContract.Callback<BaseBean<Any>> {
override fun success(data: BaseBean<Any>) {
view?.apply {
hideLoading()
bindDeviceSuccess(data.message.ifBlank { "设备绑定成功" })
}
}
override fun fail(message: String) {
view?.apply {
hideLoading()
showError(message)
}
}
})
}
/** 根据设备编号修改设备名称。 */
fun renameDevice(deviceNo: String, name: String) {
val currentView = view ?: return
val trimmedName = name.trim()
if (deviceNo.isBlank() || trimmedName.isBlank()) {
currentView.showError("设备编号和名称不能为空")
return
}
currentView.showLoading()
val json = JSONObject().apply {
put("deviceNo", deviceNo)
put("name", trimmedName)
}
deviceImpl.renameDevice(deviceNo, json, object : DeviceContract.Callback<BaseBean<Any>> {
override fun success(data: BaseBean<Any>) {
view?.apply {
hideLoading()
renameDeviceSuccess(data.message.ifBlank { "设备重命名成功" })
}
}
override fun fail(message: String) {
view?.apply {
hideLoading()
showError(message)
}
}
})
}
/** 根据设备编号发起解绑请求。 */
fun unbindDevice(deviceNo: String) {
val currentView = view ?: return
if (deviceNo.isBlank()) {
currentView.showError("设备编号不能为空")
return
}
currentView.showLoading()
deviceImpl.unbindDevice(deviceNo, object : DeviceContract.Callback<BaseBean<Any>> {
override fun success(data: BaseBean<Any>) {
view?.apply {
hideLoading()
unbindDeviceSuccess(data.message.ifBlank { "设备解绑成功" })
}
}
override fun fail(message: String) {
view?.apply {
hideLoading()
showError(message)
}
}
})
}
/** 销毁页面时取消设备模块发起的网络请求。 */
override fun onDestroy() {
deviceImpl.cancel()
super.onDestroy()
}
}