扫一扫
This commit is contained in:
@@ -70,4 +70,12 @@ dependencies {
|
||||
implementation 'io.github.scwang90:refresh-layout-kernel:2.1.1'
|
||||
implementation 'io.github.scwang90:refresh-header-classics:2.1.1'
|
||||
implementation 'io.github.scwang90:refresh-footer-classics:2.1.1'
|
||||
|
||||
implementation 'com.github.maning0303:MNMLKitScanner:V1.0.4'
|
||||
//下面版本自己控制,barcode-scanning>=17.0.2
|
||||
implementation 'com.google.mlkit:barcode-scanning:17.0.2'
|
||||
implementation "androidx.camera:camera-core:1.0.2"
|
||||
implementation "androidx.camera:camera-camera2:1.0.2"
|
||||
implementation "androidx.camera:camera-lifecycle:1.0.2"
|
||||
implementation "androidx.camera:camera-view:1.0.0-alpha25"
|
||||
}
|
||||
|
||||
@@ -8,6 +8,10 @@
|
||||
<uses-permission android:name="android.permission.CAMERA" />
|
||||
<uses-permission android:name="android.permission.RECORD_AUDIO" />
|
||||
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
|
||||
<uses-permission
|
||||
android:name="android.permission.READ_EXTERNAL_STORAGE"
|
||||
android:maxSdkVersion="32" />
|
||||
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
|
||||
|
||||
<application
|
||||
android:name=".MyApplication"
|
||||
@@ -69,6 +73,14 @@
|
||||
android:name=".device.BindDeviceActivity"
|
||||
android:exported="false" />
|
||||
|
||||
<activity
|
||||
android:name=".device.DeviceDetailActivity"
|
||||
android:exported="false" />
|
||||
|
||||
<activity
|
||||
android:name=".mine.ModifyPsdActivity"
|
||||
android:exported="false" />
|
||||
|
||||
|
||||
|
||||
<provider
|
||||
|
||||
22
app/src/main/java/com/juhabi/birds/device/DeviceAdapter.kt
Normal file
22
app/src/main/java/com/juhabi/birds/device/DeviceAdapter.kt
Normal 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 ?: "暂无地址"}")
|
||||
}
|
||||
}
|
||||
@@ -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) {
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -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))
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
}
|
||||
}
|
||||
}
|
||||
276
app/src/main/java/com/juhabi/birds/device/mvp/DeviceImpl.kt
Normal file
276
app/src/main/java/com/juhabi/birds/device/mvp/DeviceImpl.kt
Normal 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)
|
||||
}
|
||||
}
|
||||
166
app/src/main/java/com/juhabi/birds/device/mvp/DevicePresenter.kt
Normal file
166
app/src/main/java/com/juhabi/birds/device/mvp/DevicePresenter.kt
Normal 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()
|
||||
}
|
||||
}
|
||||
@@ -2,17 +2,14 @@ package com.juhabi.birds.main
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import androidx.annotation.ColorRes
|
||||
import androidx.core.content.ContextCompat
|
||||
import com.juhabi.birds.R
|
||||
import com.juhabi.birds.base.BaseActivity
|
||||
import com.juhabi.birds.databinding.ActivityMainBinding
|
||||
import com.juhabi.birds.main.adapter.MainPagerAdapter
|
||||
import com.juhabi.birds.main.fragment.HistoryFragment
|
||||
import com.juhabi.birds.device.IndexFragment
|
||||
import com.juhabi.birds.device.DeviceFragment
|
||||
import com.juhabi.birds.mine.MineFragment
|
||||
import com.juhabi.birds.main.fragment.PlanFragment
|
||||
import com.juhabi.birds.main.model.AppVersionInfo
|
||||
import com.juhabi.birds.main.mvp.MainContract
|
||||
import com.juhabi.birds.main.mvp.MainImpl
|
||||
@@ -42,7 +39,7 @@ class MainActivity : BaseActivity<ActivityMainBinding, MainPresenter>(),
|
||||
override fun initView(savedInstanceState: Bundle?) {
|
||||
binding.viewPager.adapter = MainPagerAdapter(
|
||||
this,
|
||||
listOf(IndexFragment(), PlanFragment(), HistoryFragment(), MineFragment())
|
||||
listOf(DeviceFragment(), MineFragment())
|
||||
)
|
||||
binding.viewPager.offscreenPageLimit = 1
|
||||
binding.viewPager.registerOnPageChangeCallback(object : androidx.viewpager2.widget.ViewPager2.OnPageChangeCallback() {
|
||||
@@ -60,8 +57,6 @@ class MainActivity : BaseActivity<ActivityMainBinding, MainPresenter>(),
|
||||
private fun onNavigationItemSelected(itemId: Int): Boolean {
|
||||
return when (itemId) {
|
||||
R.id.navigation_home -> onHomeSelected()
|
||||
R.id.navigation_plan -> onPlanSelected()
|
||||
R.id.navigation_history -> onHistorySelected()
|
||||
R.id.navigation_mine -> onMineSelected()
|
||||
else -> false
|
||||
}
|
||||
@@ -69,25 +64,11 @@ class MainActivity : BaseActivity<ActivityMainBinding, MainPresenter>(),
|
||||
|
||||
private fun onHomeSelected(): Boolean {
|
||||
switchPage(PAGE_HOME)
|
||||
// Add Home-specific status bar, refresh, or Fragment communication here.
|
||||
return true
|
||||
}
|
||||
|
||||
private fun onPlanSelected(): Boolean {
|
||||
switchPage(PAGE_PLAN)
|
||||
// Add Plan-specific status bar, refresh, or Fragment communication here.
|
||||
return true
|
||||
}
|
||||
|
||||
private fun onHistorySelected(): Boolean {
|
||||
switchPage(PAGE_HISTORY)
|
||||
// Add History-specific status bar, refresh, or Fragment communication here.
|
||||
return true
|
||||
}
|
||||
|
||||
private fun onMineSelected(): Boolean {
|
||||
switchPage(PAGE_MINE)
|
||||
// Add Mine-specific status bar, refresh, or Fragment communication here.
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -99,18 +80,8 @@ class MainActivity : BaseActivity<ActivityMainBinding, MainPresenter>(),
|
||||
}
|
||||
|
||||
private fun updateTopBarColor(position: Int) {
|
||||
if (position == PAGE_PLAN) {
|
||||
binding.immersiveTopImageView.visibility = View.VISIBLE
|
||||
binding.statusBarView.setBackgroundColor(
|
||||
ContextCompat.getColor(this, android.R.color.transparent)
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
binding.immersiveTopImageView.visibility = View.GONE
|
||||
@ColorRes val colorRes = when (position) {
|
||||
PAGE_HOME -> R.color.color_zhu
|
||||
PAGE_HISTORY -> R.color.color_E9A01C
|
||||
PAGE_MINE -> R.color.color_B85CE6
|
||||
else -> R.color.color_zhu
|
||||
}
|
||||
@@ -131,8 +102,6 @@ class MainActivity : BaseActivity<ActivityMainBinding, MainPresenter>(),
|
||||
|
||||
private companion object {
|
||||
const val PAGE_HOME = 0
|
||||
const val PAGE_PLAN = 1
|
||||
const val PAGE_HISTORY = 2
|
||||
const val PAGE_MINE = 3
|
||||
const val PAGE_MINE = 1
|
||||
}
|
||||
}
|
||||
|
||||
@@ -167,14 +167,6 @@ class MineCenterActivity : BaseActivity<ActivityMineCenterBinding, MinePresenter
|
||||
appUpdateManager.checkAndUpdate(currentVersion, versionInfo)
|
||||
}
|
||||
|
||||
override fun onRequestPermissionsResult(
|
||||
requestCode: Int,
|
||||
permissions: Array<out String>,
|
||||
grantResults: IntArray
|
||||
) {
|
||||
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
|
||||
appUpdateManager.onRequestPermissionsResult(requestCode, grantResults)
|
||||
}
|
||||
|
||||
/**
|
||||
* 日期、时间和日期时间共用同一个组件调用入口。
|
||||
@@ -211,6 +203,16 @@ class MineCenterActivity : BaseActivity<ActivityMineCenterBinding, MinePresenter
|
||||
showMessage(message)
|
||||
}
|
||||
|
||||
override fun onRequestPermissionsResult(
|
||||
requestCode: Int,
|
||||
permissions: Array<out String>,
|
||||
grantResults: IntArray
|
||||
) {
|
||||
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
|
||||
appUpdateManager.onRequestPermissionsResult(requestCode, grantResults)
|
||||
}
|
||||
|
||||
|
||||
override fun onPermissionsGranted(permissions: Array<String>) {
|
||||
showMessage("权限获取成功:${permissions.joinToString { it.substringAfterLast('.') }}")
|
||||
}
|
||||
|
||||
130
app/src/main/java/com/juhabi/birds/mine/ModifyPsdActivity.kt
Normal file
130
app/src/main/java/com/juhabi/birds/mine/ModifyPsdActivity.kt
Normal file
@@ -0,0 +1,130 @@
|
||||
package com.juhabi.birds.mine
|
||||
|
||||
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.ActivityMineCenterBinding
|
||||
import com.juhabi.birds.databinding.ModifyPsdLayoutBinding
|
||||
import com.juhabi.birds.main.model.AppVersionInfo
|
||||
import com.juhabi.birds.mine.model.MineUserInfo
|
||||
import com.juhabi.birds.mine.mvp.MineContract
|
||||
import com.juhabi.birds.mine.mvp.MineImpl
|
||||
import com.juhabi.birds.mine.mvp.MinePresenter
|
||||
import com.juhabi.birds.util.ToastUtil
|
||||
|
||||
/**
|
||||
* 旧密码,修改新密码
|
||||
*/
|
||||
class ModifyPsdActivity : BaseActivity<ModifyPsdLayoutBinding, MinePresenter>(),
|
||||
MineContract.MineView, View.OnClickListener {
|
||||
override fun createBinding(inflater: LayoutInflater): ModifyPsdLayoutBinding {
|
||||
return ModifyPsdLayoutBinding.inflate(inflater)
|
||||
}
|
||||
|
||||
override fun createPresenter(): MinePresenter? {
|
||||
return MinePresenter(MineImpl())
|
||||
}
|
||||
|
||||
override fun initWindows() {
|
||||
ImmersionBar.with(this)
|
||||
.statusBarView(binding.statusBarView)
|
||||
.statusBarDarkFont(true)
|
||||
.navigationBarColor(R.color.color_zhu)
|
||||
.navigationBarDarkIcon(true)
|
||||
.init()
|
||||
}
|
||||
|
||||
|
||||
override fun initView(savedInstanceState: Bundle?) {
|
||||
listOf(
|
||||
binding.oldPsdVisibilityButton,
|
||||
binding.passwordVisibilityButton,
|
||||
binding.confirmPasswordVisibilityButton,
|
||||
binding.confirmButton,
|
||||
binding.includedTitle.imgBack,
|
||||
).forEach { button ->
|
||||
button.setOnClickListener(this)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onClick(view: View) {
|
||||
when(view.id){
|
||||
R.id.img_back ->{
|
||||
finish()
|
||||
}
|
||||
|
||||
R.id.confirmButton ->{//确认修改
|
||||
val oldPassword = binding.oldPasswordEditText.text.toString()
|
||||
val newPassword = binding.passwordEditText.text.toString()
|
||||
val confirmPassword = binding.confirmPasswordEditText.text.toString()
|
||||
|
||||
// 按顺序校验输入项,避免无效数据提交到服务端。
|
||||
if (oldPassword.isBlank()) {
|
||||
ToastUtil.showShort(mContext, "当前密码不能为空")
|
||||
return
|
||||
}
|
||||
if (newPassword.isBlank()) {
|
||||
ToastUtil.showShort(mContext, "新密码不能为空")
|
||||
return
|
||||
}
|
||||
if (confirmPassword.isBlank()) {
|
||||
ToastUtil.showShort(mContext, "请再次输入新密码")
|
||||
return
|
||||
}
|
||||
if (oldPassword == newPassword) {
|
||||
ToastUtil.showShort(mContext, "新密码不能与当前密码相同")
|
||||
return
|
||||
}
|
||||
if (newPassword != confirmPassword) {
|
||||
ToastUtil.showShort(mContext, "两次输入的新密码不一致")
|
||||
return
|
||||
}
|
||||
if (!isValidPassword(newPassword)) {
|
||||
ToastUtil.showShort(mContext, "密码需为8-20位,且包含大小写字母和数字")
|
||||
return
|
||||
}
|
||||
|
||||
presenter?.modifyPassword(oldPassword, newPassword)
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
override fun showError(message: String) {
|
||||
hideLoading()
|
||||
ToastUtil.showShort(mContext, message)
|
||||
}
|
||||
|
||||
override fun onModifyPasswordSuccess(message: String) {
|
||||
hideLoading()
|
||||
ToastUtil.showShort(mContext, message.ifBlank { "密码修改成功" })
|
||||
finish()
|
||||
}
|
||||
|
||||
/** 8-20 位,且同时包含大写、小写字母和数字。 */
|
||||
private fun isValidPassword(password: String): Boolean {
|
||||
if (password.length !in PASSWORD_MIN_LENGTH..PASSWORD_MAX_LENGTH) return false
|
||||
return password.any { it.isUpperCase() } &&
|
||||
password.any { it.isLowerCase() } &&
|
||||
password.any { it.isDigit() }
|
||||
}
|
||||
|
||||
private companion object {
|
||||
const val PASSWORD_MIN_LENGTH = 8
|
||||
const val PASSWORD_MAX_LENGTH = 20
|
||||
}
|
||||
|
||||
override fun getUserInfoSuccess(userInfo: MineUserInfo) {
|
||||
}
|
||||
|
||||
override fun onVersionChecked(versionInfo: AppVersionInfo) {
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -16,11 +16,14 @@ interface MineContract {
|
||||
params: Map<String, String>,
|
||||
callback: Callback<AppVersionInfo>
|
||||
)
|
||||
fun modifyPassword(oldPassword: String, newPassword: String, callback: Callback<String>)
|
||||
fun cancel()
|
||||
}
|
||||
|
||||
interface MineView : IView {
|
||||
fun getUserInfoSuccess(userInfo: MineUserInfo)
|
||||
fun onVersionChecked(versionInfo: AppVersionInfo)
|
||||
/** 修改密码成功回调,非修改密码页面无需处理。 */
|
||||
fun onModifyPasswordSuccess(message: String) {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,11 +10,13 @@ import com.juhabi.birds.network.NetworkManager
|
||||
import retrofit2.Call
|
||||
import retrofit2.Callback
|
||||
import retrofit2.Response
|
||||
import org.json.JSONObject
|
||||
|
||||
class MineImpl : MineContract.DataModel {
|
||||
private val mainHandler = Handler(Looper.getMainLooper())
|
||||
private var userInfoCall: Call<BaseBean<MineUserInfo>>? = null
|
||||
private var versionCall: Call<BaseBean<AppVersionInfo>>? = null
|
||||
private var modifyPasswordCall: Call<BaseBean<Any>>? = null
|
||||
|
||||
override fun getUserInfo(
|
||||
params: Map<String, String>,
|
||||
@@ -96,11 +98,53 @@ class MineImpl : MineContract.DataModel {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过旧密码修改原密码
|
||||
*/
|
||||
override fun modifyPassword(
|
||||
oldPassword: String,
|
||||
newPassword: String,
|
||||
callback: MineContract.Callback<String>
|
||||
) {
|
||||
modifyPasswordCall?.cancel()
|
||||
val requestBody = JSONObject().apply {
|
||||
put("oldPassword", oldPassword)
|
||||
put("newPassword", newPassword)
|
||||
}
|
||||
modifyPasswordCall = ApiClient.apiService
|
||||
.modifyPassword(NetworkManager.getToken(), NetworkManager.toRequestBody(requestBody))
|
||||
.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 == 200) {
|
||||
callback.success(bean.message.orEmpty())
|
||||
} 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() {
|
||||
userInfoCall?.cancel()
|
||||
userInfoCall = null
|
||||
versionCall?.cancel()
|
||||
versionCall = null
|
||||
modifyPasswordCall?.cancel()
|
||||
modifyPasswordCall = null
|
||||
mainHandler.removeCallbacksAndMessages(null)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,6 +46,27 @@ class MinePresenter(private val mineImpl: MineImpl) : BasePresenter<MineContract
|
||||
)
|
||||
}
|
||||
|
||||
/** 提交修改密码请求,统一处理加载状态和接口结果。 */
|
||||
fun modifyPassword(oldPassword: String, newPassword: String) {
|
||||
val currentView = view ?: return
|
||||
currentView.showLoading()
|
||||
mineImpl.modifyPassword(oldPassword, newPassword, object : MineContract.Callback<String> {
|
||||
override fun success(data: String) {
|
||||
view?.apply {
|
||||
hideLoading()
|
||||
onModifyPasswordSuccess(data)
|
||||
}
|
||||
}
|
||||
|
||||
override fun fail(message: String) {
|
||||
view?.apply {
|
||||
hideLoading()
|
||||
showError(message)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
mineImpl.cancel()
|
||||
super.onDestroy()
|
||||
|
||||
@@ -126,6 +126,15 @@ interface ApiService {
|
||||
fun getBindDeviceList(@HeaderMap headers: Map<String, String>, @QueryMap params: Map<String, String>): Call<BaseBean<List<DeviceEntity>>>
|
||||
|
||||
|
||||
/**
|
||||
* 获取设备详情
|
||||
*
|
||||
*/
|
||||
@GET("app/device/{deviceNo}")
|
||||
fun getDeviceDetail(@HeaderMap headers: Map<String, String>, @Path("deviceNo") deviceNo: String): Call<BaseBean<DeviceEntity>>
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 绑定设备
|
||||
{
|
||||
@@ -173,6 +182,20 @@ interface ApiService {
|
||||
fun getMineUserInfo(@HeaderMap headers: Map<String, String>, @QueryMap params: Map<String, String>): Call<BaseBean<MineUserInfo>>
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 通过旧密码修改新密码
|
||||
{
|
||||
"oldPassword": "string",
|
||||
"newPassword": "string"
|
||||
}
|
||||
*/
|
||||
@PUT("app/user/password")
|
||||
fun modifyPassword(@HeaderMap headers: Map<String, String>, @Body requestBody: RequestBody): Call<BaseBean<Any>>
|
||||
|
||||
|
||||
|
||||
|
||||
@GET("app/v1/checkVersion")
|
||||
fun checkVersionCode(@HeaderMap headers: Map<String, String>, @QueryMap params: Map<String, String>): Call<BaseBean<AppVersionInfo>>
|
||||
}
|
||||
|
||||
@@ -4,18 +4,6 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/immersiveTopImageView"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="320dp"
|
||||
android:contentDescription="@string/plan_top_image_description"
|
||||
android:scaleType="centerCrop"
|
||||
android:src="@drawable/img_top"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<View
|
||||
android:id="@+id/statusBarView"
|
||||
android:layout_width="0dp"
|
||||
|
||||
33
app/src/main/res/layout/device_detail_layout.xml
Normal file
33
app/src/main/res/layout/device_detail_layout.xml
Normal file
@@ -0,0 +1,33 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
<View
|
||||
android:id="@+id/statusBarView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:background="@color/color_zhu" />
|
||||
<RelativeLayout
|
||||
android:id="@+id/rl_title"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="48dp">
|
||||
|
||||
<include
|
||||
android:id="@+id/included_title"
|
||||
layout="@layout/include_activity_back_gray_layout" />
|
||||
</RelativeLayout>
|
||||
<TextView
|
||||
android:id="@+id/tv_bind"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="10dp"
|
||||
android:textSize="15sp"
|
||||
android:text="绑定"
|
||||
android:background="@color/color_zhu"/>
|
||||
|
||||
<Switch
|
||||
android:id="@+id/switch_ctrl"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"/>
|
||||
</LinearLayout>
|
||||
@@ -20,12 +20,6 @@
|
||||
android:src="@mipmap/ic_launcher"
|
||||
android:layout_gravity="end"/>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/control_eye"
|
||||
android:layout_width="30dp"
|
||||
android:layout_height="30dp"
|
||||
android:src="@mipmap/icon_login_show"
|
||||
android:layout_gravity="end"/>
|
||||
</LinearLayout>
|
||||
|
||||
|
||||
|
||||
@@ -14,13 +14,27 @@
|
||||
android:id="@+id/included_title"
|
||||
layout="@layout/include_title_home_layout" />
|
||||
</RelativeLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
<TextView
|
||||
android:id="@+id/tv_index"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:text="@string/home_page_title"
|
||||
android:textColor="@color/black"
|
||||
android:textSize="24sp" />
|
||||
<ImageView
|
||||
android:id="@+id/img_scan"
|
||||
android:layout_width="30dp"
|
||||
android:layout_height="30dp"
|
||||
android:src="@mipmap/ic_launcher"
|
||||
android:layout_marginEnd="15dp"/>
|
||||
</LinearLayout>
|
||||
|
||||
<com.scwang.smart.refresh.layout.SmartRefreshLayout
|
||||
android:id="@+id/refresh"
|
||||
android:layout_width="match_parent"
|
||||
|
||||
212
app/src/main/res/layout/modify_psd_layout.xml
Normal file
212
app/src/main/res/layout/modify_psd_layout.xml
Normal file
@@ -0,0 +1,212 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
android:background="@color/color_zhu">
|
||||
<View
|
||||
android:id="@+id/statusBarView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:background="@color/color_zhu" />
|
||||
<RelativeLayout
|
||||
android:id="@+id/rl_title"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="48dp">
|
||||
|
||||
<include
|
||||
android:id="@+id/included_title"
|
||||
layout="@layout/include_activity_back_gray_layout" />
|
||||
</RelativeLayout>
|
||||
|
||||
<ScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1"
|
||||
android:fillViewport="true">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:paddingStart="20dp"
|
||||
android:paddingEnd="20dp"
|
||||
android:paddingBottom="32dp">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="50dp"
|
||||
android:layout_marginTop="20dp"
|
||||
android:gravity="center_vertical"
|
||||
android:background="@drawable/shape_login_field_bg"
|
||||
android:paddingStart="44dp"
|
||||
android:paddingEnd="16dp">
|
||||
|
||||
<EditText
|
||||
android:id="@+id/oldPasswordEditText"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1"
|
||||
android:background="@null"
|
||||
android:hint="请输入当前密码"
|
||||
android:inputType="textPassword"
|
||||
android:textColorHint="@color/color_999999"
|
||||
android:textSize="16sp" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/oldPsdVisibilityButton"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:src="@mipmap/icon_login_show" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="50dp"
|
||||
android:layout_marginTop="20dp"
|
||||
android:gravity="center_vertical"
|
||||
android:background="@drawable/shape_login_field_bg"
|
||||
android:paddingStart="44dp"
|
||||
android:paddingEnd="16dp">
|
||||
|
||||
<EditText
|
||||
android:id="@+id/passwordEditText"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1"
|
||||
android:background="@null"
|
||||
android:hint="请输入8-20位密码"
|
||||
android:inputType="textPassword"
|
||||
android:textColorHint="@color/color_999999"
|
||||
android:textSize="16sp" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/passwordVisibilityButton"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:src="@mipmap/icon_login_show"
|
||||
android:contentDescription="显示密码" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="50dp"
|
||||
android:layout_marginTop="20dp"
|
||||
android:gravity="center_vertical"
|
||||
android:background="@drawable/shape_login_field_bg"
|
||||
android:paddingStart="44dp"
|
||||
android:paddingEnd="16dp">
|
||||
|
||||
<EditText
|
||||
android:id="@+id/confirmPasswordEditText"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1"
|
||||
android:background="@null"
|
||||
android:hint="请再次输入新密码"
|
||||
android:inputType="textPassword"
|
||||
android:textColorHint="@color/color_999999"
|
||||
android:textSize="16sp" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/confirmPasswordVisibilityButton"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:src="@mipmap/icon_login_show"
|
||||
android:contentDescription="显示密码" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="26dp"
|
||||
android:layout_marginEnd="26dp"
|
||||
android:layout_marginTop="36dp"
|
||||
android:orientation="vertical"
|
||||
android:paddingStart="18dp"
|
||||
android:paddingVertical="10dp"
|
||||
android:background="@drawable/shape_register_rule_bg">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="密码规则"
|
||||
android:textColor="@color/color_white"
|
||||
android:textSize="18sp" />
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center">
|
||||
<ImageView
|
||||
android:layout_width="15dp"
|
||||
android:layout_height="15dp"
|
||||
android:layout_marginVertical="5dp"
|
||||
android:src="@mipmap/icon_login_select"/>
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="6dp"
|
||||
android:text="长度在8-20个字符之间"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="14sp" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center">
|
||||
<ImageView
|
||||
android:layout_width="15dp"
|
||||
android:layout_height="15dp"
|
||||
android:layout_marginVertical="5dp"
|
||||
android:src="@mipmap/icon_login_select"/>
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="6dp"
|
||||
android:text="包含大小写字母和数字"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="14sp" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center">
|
||||
<ImageView
|
||||
android:layout_width="15dp"
|
||||
android:layout_height="15dp"
|
||||
android:layout_marginVertical="5dp"
|
||||
android:src="@mipmap/icon_login_tips"/>
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="6dp"
|
||||
android:text="不能与当前密码相同"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="14sp" />
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/confirmButton"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="40dp"
|
||||
android:layout_marginStart="5dp"
|
||||
android:layout_marginEnd="5dp"
|
||||
android:layout_marginTop="84dp"
|
||||
android:gravity="center"
|
||||
android:background="@drawable/shape_login_field_bg"
|
||||
android:backgroundTint="@null"
|
||||
android:stateListAnimator="@null"
|
||||
android:text="确定"
|
||||
android:textAllCaps="false"
|
||||
android:textColor="@color/color_zhu"
|
||||
android:textSize="17sp" />
|
||||
</LinearLayout>
|
||||
</ScrollView>
|
||||
</LinearLayout>
|
||||
53
app/src/main/res/layout/popup_device_action.xml
Normal file
53
app/src/main/res/layout/popup_device_action.xml
Normal file
@@ -0,0 +1,53 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:padding="6dp">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/action_rename"
|
||||
android:layout_width="130dp"
|
||||
android:layout_height="42dp"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal"
|
||||
android:paddingHorizontal="10dp">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="22dp"
|
||||
android:layout_height="22dp"
|
||||
android:src="@mipmap/ic_launcher"
|
||||
android:contentDescription="@string/device_action_rename" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="10dp"
|
||||
android:text="重命名"
|
||||
android:textColor="#FFFFFF"
|
||||
android:textSize="16sp" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/action_unbind"
|
||||
android:layout_width="130dp"
|
||||
android:layout_height="42dp"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal"
|
||||
android:paddingHorizontal="10dp">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="22dp"
|
||||
android:layout_height="22dp"
|
||||
android:src="@mipmap/ic_launcher"
|
||||
android:contentDescription="@string/device_action_unbind" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="10dp"
|
||||
android:text="解绑设备"
|
||||
android:textColor="#FFFFFF"
|
||||
android:textSize="16sp" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
@@ -4,16 +4,6 @@
|
||||
android:id="@+id/navigation_home"
|
||||
android:icon="@drawable/ic_nav_home"
|
||||
android:title="@string/tab_home" />
|
||||
<item
|
||||
android:id="@+id/navigation_plan"
|
||||
android:icon="@drawable/ic_nav_plan"
|
||||
android:title="@string/tab_plan"
|
||||
android:visible="false"/>
|
||||
<item
|
||||
android:id="@+id/navigation_history"
|
||||
android:icon="@drawable/ic_nav_history"
|
||||
android:title="@string/tab_history"
|
||||
android:visible="false"/>
|
||||
<item
|
||||
android:id="@+id/navigation_mine"
|
||||
android:icon="@drawable/ic_nav_mine"
|
||||
|
||||
@@ -67,6 +67,7 @@
|
||||
<string name="home_page_title">首页</string>
|
||||
<string name="device_action_rename">重命名</string>
|
||||
<string name="device_action_unbind">解绑设备</string>
|
||||
<string name="cancel_scan">取消扫码</string>
|
||||
<string name="plan_page_title">计划</string>
|
||||
<string name="history_page_title">历史</string>
|
||||
<string name="mine_page_title">我的</string>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
# http://www.gradle.org/docs/current/userguide/build_environment.html
|
||||
# Specifies the JVM arguments used for the daemon process.
|
||||
# The setting is particularly useful for tweaking memory settings.
|
||||
org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
|
||||
org.gradle.jvmargs=-Xmx4096m -Dfile.encoding=UTF-8
|
||||
# When configured, Gradle will run in incubating parallel mode.
|
||||
# This option should only be used with decoupled projects. For more details, visit
|
||||
# https://developer.android.com/r/tools/gradle-multi-project-decoupled-projects
|
||||
|
||||
Reference in New Issue
Block a user