diff --git a/app/src/main/java/com/juhabi/birds/device/DeviceDetailActivity.kt b/app/src/main/java/com/juhabi/birds/device/DeviceDetailActivity.kt index 9238a7b..34e3c4f 100644 --- a/app/src/main/java/com/juhabi/birds/device/DeviceDetailActivity.kt +++ b/app/src/main/java/com/juhabi/birds/device/DeviceDetailActivity.kt @@ -26,6 +26,8 @@ class DeviceDetailActivity : } private var device: DeviceEntity? = null + /** 当前正在提交的眼罩目标状态,0 表示打开,1 表示关闭。 */ + private var pendingCoverOn: Boolean? = null override fun createBinding(inflater: LayoutInflater): DeviceDetailLayoutBinding { return DeviceDetailLayoutBinding.inflate(inflater) } @@ -38,7 +40,7 @@ class DeviceDetailActivity : ImmersionBar.with(this) .statusBarView(binding.statusBarView) .statusBarDarkFont(true) - .navigationBarColor(R.color.color_zhu) + .navigationBarColor(R.color.white) .navigationBarDarkIcon(true) .init() } @@ -46,7 +48,7 @@ class DeviceDetailActivity : override fun initView(savedInstanceState: Bundle?) { device = intent.getSerializableExtra(EXTRA_DEVICE) as? DeviceEntity - binding.includedTitle.tvTitle.text = device?.name ?: "设备详情" + binding.includeTitle.tvTitle.text = device?.name ?: "设备详情" val deviceNo = device?.deviceNo if (deviceNo.isNullOrBlank()) { showError("设备信息无效") @@ -57,7 +59,8 @@ class DeviceDetailActivity : listOf( binding.tvBind, binding.switchCtrl, - binding.includedTitle.imgBack, + binding.imgSwitchCtrl, + binding.includeTitle.imgBack, ).forEach { button -> button.setOnClickListener(this) } @@ -68,16 +71,23 @@ class DeviceDetailActivity : R.id.img_back ->{ finish() } - R.id.switch_ctrl ->{ - if(device?.status == "0"){ - showMessage("设备已经离线") - return; - } + + R.id.img_switch_ctrl ->{ val currentDevice = device ?: return - // hoodCoverOn:0 表示打开,1 表示关闭;点击后发送切换后的目标状态。 + if (currentDevice.status == "0") { + showMessage("设备已经离线") + return + } + + // 根据当前服务端状态计算目标状态,再由图片按钮统一发起控制请求。 val coverOn = currentDevice.hoodCoverOn != HOOD_COVER_ON + pendingCoverOn = coverOn presenter?.controlEye(currentDevice.deviceNo, coverOn) } + + R.id.switch_ctrl ->{ + // 开关控件仅保留展示用途,实际控制统一由图片按钮处理。 + } } } @@ -91,20 +101,31 @@ class DeviceDetailActivity : override fun getDeviceDetailSuccess(device: DeviceEntity) { this.device = device - binding.includedTitle.tvTitle.text = device.name + binding.includeTitle.tvTitle.text = device.name binding.tvBind.text = "设备编号:${device.deviceNo}" - binding.switchCtrl.isChecked = device.hoodCoverOn == HOOD_COVER_ON + updateCoverControlState(device.hoodCoverOn == HOOD_COVER_ON) } override fun controlEyeSuccess(message: String) { showMessage(message) + val coverOn = pendingCoverOn ?: return + pendingCoverOn = null device = device?.copy( - hoodCoverOn = if (binding.switchCtrl.isChecked) { + hoodCoverOn = if (coverOn) { HOOD_COVER_ON } else { HOOD_COVER_OFF } ) + updateCoverControlState(coverOn) + } + + /** 同步开关控件和图片按钮的视觉状态,避免请求成功前出现错误状态。 */ + private fun updateCoverControlState(coverOn: Boolean) { + binding.switchCtrl.isChecked = coverOn + binding.imgSwitchCtrl.setImageResource( + if (coverOn) R.mipmap.icon_switch_on else R.mipmap.icon_switch_off + ) } override fun bindDeviceSuccess(message: String) { diff --git a/app/src/main/java/com/juhabi/birds/login/LoginActivity.kt b/app/src/main/java/com/juhabi/birds/login/LoginActivity.kt index f2ea6af..c30f436 100644 --- a/app/src/main/java/com/juhabi/birds/login/LoginActivity.kt +++ b/app/src/main/java/com/juhabi/birds/login/LoginActivity.kt @@ -55,6 +55,7 @@ class LoginActivity : BaseActivity(), binding.getCodeButton, binding.loginButton, binding.registerButton, + binding.tvChangeLoginType, ).forEach { button -> button.setOnClickListener(this) } @@ -63,6 +64,7 @@ class LoginActivity : BaseActivity(), override fun onClick(view: View) { when (view.id) { R.id.tvChangeCode -> {//更换登录方式 + //Todo 2.目前这里的切换,只是针对的手机 验证码/密码登录,两种方式。需要修改一下,参考【Todo 1】这里不仅要对手机号的 验证码/密码登录 做处理,同时也要处理 邮箱的 验证码/密码登录;所以这里应该是四种登录方式。 手机验证码/密码 + 邮箱验证码/密码。 if (loginByPassword) { binding.linearCode.visibility = View.VISIBLE binding.linearPsd.visibility = View.GONE @@ -76,7 +78,13 @@ class LoginActivity : BaseActivity(), } } + R.id.tv_change_login_type ->{ + //Todo 1.这里是切换手机/邮箱 两种登录方式的按钮,点击切换,同时img_login_type改变,【icon_login_phone】/【icon_login_email】两种。 + + } + R.id.getCodeButton -> {//获取验证码 + //Todo 3. 这里获取验证码,是2种,发送手机验证码和邮箱验证码。另外我【Apiservice】中已经写好了方法,两种获取验证码的接口都有。 val phone = binding.accountEditText.text.toString().trim() if (phone.isBlank()) { ToastUtil.showShort(mContext, "手机号不能为空") @@ -98,6 +106,7 @@ class LoginActivity : BaseActivity(), } R.id.loginButton -> {//登录 + //Todo 4.同样也是和前面的逻辑一样,4种登录方法。需要你补齐目前缺少的。 val account = binding.accountEditText.text.toString().trim() if (account.isBlank()) { ToastUtil.showShort(mContext, "账号不能为空") diff --git a/app/src/main/java/com/juhabi/birds/mine/MineFragment.kt b/app/src/main/java/com/juhabi/birds/mine/MineFragment.kt index 4c4dc95..719f49e 100644 --- a/app/src/main/java/com/juhabi/birds/mine/MineFragment.kt +++ b/app/src/main/java/com/juhabi/birds/mine/MineFragment.kt @@ -86,6 +86,27 @@ class MineFragment : BaseFragment(), } + //注销账号 + R.id.linear_zxzh -> { + showDialogtext( + "确认注销账号?", + "注销账号后,账号将被删除,所有设备" + + "解绑、个人信息被清除,无法恢复,请谨慎操作?", + "确认注销", + "取消", + object : OnDialogClick { + override fun onCancelClick() { + } + + override fun onConfirmClick() { + // 用户确认后调用服务端注销账号接口。 + presenter?.userLogoff() + } + } + ) + } + + //退出登录 R.id.tv_logout -> { showDialogtext( @@ -133,6 +154,9 @@ class MineFragment : BaseFragment(), showMessage(message) } + /** + * 退出登录 + */ override fun onLogoutSuccess(message: String) { val context = context ?: return // 服务端退出成功后清除本地账号信息,避免应用重启后恢复旧登录状态。 @@ -144,6 +168,21 @@ class MineFragment : BaseFragment(), goToClearTask(LoginActivity::class.java, null) } + + /** + * 注销账号 + */ + override fun onLogoffSuccess(message: String) { + val context = context ?: return + // 注销成功后清理本地会话,避免已注销账号继续被本地恢复。 + UserInfoManager.clearUser(context) + AppConfig.USER_LOGIN = false + showMessage(message) + + // 注销后清空页面栈并回到登录页。 + goToClearTask(LoginActivity::class.java, null) + } + /** 查询应用缓存大小,并将结果显示在个人中心页面。 */ private fun displayCacheSize() { val context = context ?: return diff --git a/app/src/main/java/com/juhabi/birds/mine/mvp/MineContract.kt b/app/src/main/java/com/juhabi/birds/mine/mvp/MineContract.kt index e992799..22afa3d 100644 --- a/app/src/main/java/com/juhabi/birds/mine/mvp/MineContract.kt +++ b/app/src/main/java/com/juhabi/birds/mine/mvp/MineContract.kt @@ -29,6 +29,7 @@ interface MineContract { callback: Callback> ) fun userLogout(callback: Callback>) + fun userLogoff(callback: Callback>) fun cancel() } @@ -47,5 +48,7 @@ interface MineContract { fun onUpdateBindNewPhoneSuccess(message: String) {} /** 退出登录成功回调。 */ fun onLogoutSuccess(message: String) {} + /** 注销账号成功回调。 */ + fun onLogoffSuccess(message: String) {} } } diff --git a/app/src/main/java/com/juhabi/birds/mine/mvp/MineImpl.kt b/app/src/main/java/com/juhabi/birds/mine/mvp/MineImpl.kt index 16836a3..522bbd6 100644 --- a/app/src/main/java/com/juhabi/birds/mine/mvp/MineImpl.kt +++ b/app/src/main/java/com/juhabi/birds/mine/mvp/MineImpl.kt @@ -23,6 +23,7 @@ class MineImpl : MineContract.DataModel { private var newPhoneCodeCall: Call>? = null private var updateBindNewPhoneCall: Call>? = null private var logoutCall: Call>? = null + private var logoffCall: Call>? = null override fun getCurrentPhoneCode( callback: MineContract.Callback> @@ -183,6 +184,33 @@ class MineImpl : MineContract.DataModel { } } + override fun userLogoff(callback: MineContract.Callback>) { + logoffCall?.cancel() + logoffCall = ApiClient.apiService + .userLogoff(NetworkManager.getToken()) + .also { call -> + call.enqueue(object : Callback> { + override fun onResponse( + call: Call>, + response: Response> + ) { + mainHandler.post { + response.body()?.let(callback::success) + ?: callback.fail("注销账号失败") + } + } + + override fun onFailure(call: Call>, error: Throwable) { + if (!call.isCanceled) { + mainHandler.post { + callback.fail(error.message ?: "网络请求失败") + } + } + } + }) + } + } + override fun getUserInfo( params: Map, callback: MineContract.Callback @@ -320,6 +348,8 @@ class MineImpl : MineContract.DataModel { updateBindNewPhoneCall = null logoutCall?.cancel() logoutCall = null + logoffCall?.cancel() + logoffCall = null mainHandler.removeCallbacksAndMessages(null) } } diff --git a/app/src/main/java/com/juhabi/birds/mine/mvp/MinePresenter.kt b/app/src/main/java/com/juhabi/birds/mine/mvp/MinePresenter.kt index c902a7b..ce5a24c 100644 --- a/app/src/main/java/com/juhabi/birds/mine/mvp/MinePresenter.kt +++ b/app/src/main/java/com/juhabi/birds/mine/mvp/MinePresenter.kt @@ -146,6 +146,31 @@ class MinePresenter(private val mineImpl: MineImpl) : BasePresenter> { + override fun success(data: BaseBean) { + view?.apply { + hideLoading() + if (data.code == 0 || data.code == 200) { + onLogoffSuccess(data.message.ifBlank { "账号注销成功" }) + } else { + showError(data.message.ifBlank { "注销账号失败" }) + } + } + } + + override fun fail(message: String) { + view?.apply { + hideLoading() + showError(message) + } + } + }) + } + fun getUserInfo() { val currentView = view ?: return currentView.showLoading() diff --git a/app/src/main/java/com/juhabi/birds/network/ApiService.kt b/app/src/main/java/com/juhabi/birds/network/ApiService.kt index c2856c8..1b82c0d 100644 --- a/app/src/main/java/com/juhabi/birds/network/ApiService.kt +++ b/app/src/main/java/com/juhabi/birds/network/ApiService.kt @@ -244,6 +244,15 @@ interface ApiService { fun userLogout(@HeaderMap headers: Map): Call> + /** + * 注销账号 + * + * 不需要任何入参, + */ + @POST("app/user/account") + fun userLogoff(@HeaderMap headers: Map): Call> + + @GET("app/v1/checkVersion") diff --git a/app/src/main/res/drawable/shape_dev_eye_ctrl_line_bg.xml b/app/src/main/res/drawable/shape_dev_eye_ctrl_line_bg.xml new file mode 100644 index 0000000..14ad348 --- /dev/null +++ b/app/src/main/res/drawable/shape_dev_eye_ctrl_line_bg.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_login.xml b/app/src/main/res/layout/activity_login.xml index c7d96df..9a23b03 100644 --- a/app/src/main/res/layout/activity_login.xml +++ b/app/src/main/res/layout/activity_login.xml @@ -55,6 +55,7 @@ android:paddingEnd="16dp"> + + layout="@layout/include_activity_back_greenbg_layout" /> + android:background="@color/white" /> - + android:layout_height="wrap_content" + android:background="@color/mainBackgroundColor"> + android:id="@+id/include_title" + layout="@layout/include_activity_back_whitebg_layout"/> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/include_activity_back_whitebg_layout.xml b/app/src/main/res/layout/include_activity_back_whitebg_layout.xml index 1e945ac..88d1815 100644 --- a/app/src/main/res/layout/include_activity_back_whitebg_layout.xml +++ b/app/src/main/res/layout/include_activity_back_whitebg_layout.xml @@ -6,7 +6,7 @@ android:id="@+id/relative_title" android:layout_width="match_parent" android:layout_height="48dp" - android:background="@color/color_zhu"> + android:background="@color/white"> #0d000000 + #1A000000 + #33000000 + #4D000000 + #66000000 + #80000000 + #99000000 + #B3000000 + #CC000000 + #E6000000 + + #FFBB86FC #FF6200EE @@ -145,4 +156,7 @@ #286F51 + + +