今天来处理登入的流程。
送出登入的方式很简单,使用WebSocketClient的send方法即可:
login.setOnClickListener {
(requireActivity() as MainActivity).showLoading(R.string.startLogin) // 显示登入中的progress
val id = idInput.text.toString() // 取得输入的帐号
val pwd = pwdInput.text.toString() // 取得输入的密码
PttClient.getInstance().send("$id\r\n$pwd\r\n".toByteArray(BIG5))
}
另外如我在Day04中提到的,Ptt预设是使用Big-5的文字编码,我也没做另外的尝试,所以这边在送出时也需要转成Big-5编码的byte array。
val BIG5 = Charset.forName("big5")!!
正常登入成功
没任何状况的话,正常登入後会先显示以下画面
帐号或密码错误
成功但重复登入(原本已有帐号登入中)
成功登入,先前有尝试登入但输入错密码的纪录
最终登入完成的画面
针对这几种Case,我使用以下几个Pattern来监听帐密送出後的回传状况:
private val loginResultPattern = arrayOf(
"请按任意键继续",
"您想删除其他重复登入的连线吗?",
"密码不对或无此帐号。请检查大小写及有无输入错误。",
"您要删除以上错误尝试的记录吗?",
"【主功能表】"
)
在上方帐密送出後,开启协程等待Ptt Server的回传。
viewLifecycleOwner.lifecycleScope.launch(Dispatchers.IO) {
while (true) {
if (isWaitingForUserReply) continue
when (PttClient.getInstance().expect(loginResultPattern)) {
// ...
}
}
}
isWaitingForUserReply的用途为後续可能会有需使用者回覆的选项,在等待回覆时我不希望去呼叫到expect方法并且重复进入when中。後续内容皆在上述协程的when内
// ...
0 -> { // "请按任意键继续"
if (hasPressedAnyKey) continue
hasPressedAnyKey = true
PttClient.getInstance().send("\r\n".toByteArray(BIG5))
delay(500L)
}
// ...
直接送出enter并等待後续的回传,为了避免重复送出导致页面跑掉,所以这边多设了一个判断Flag。
// ...
2 -> { // "密码不对或无此帐号。请检查大小写及有无输入错误。"
withContext(Dispatchers.Main) {
(requireActivity() as MainActivity).dismissLoading()
AlertDialog.Builder(requireContext())
.setTitle("密码不对或无此帐号。")
.setMessage("请检查大小写及有无输入错误。")
.setPositiveButton(android.R.string.ok, null)
.show()
}
break
}
// ...
跳出AlertDialog告知使用者、关闭loading状态,并且退出此协程。
// ...
1 -> { // "您想删除其他重复登入的连线吗?[Y/n]"
if (hasAskedForMultiUsers) continue
hasAskedForMultiUsers = true
isWaitingForUserReply = true
withContext(Dispatchers.Main) {
(requireActivity() as MainActivity).dismissLoading()
AlertDialog.Builder(requireContext())
.setTitle("注意: 您有其它连线已登入此帐号。")
.setMessage("您想删除其他重复登入的连线吗?")
.setPositiveButton("是") { _, _ ->
(requireActivity() as MainActivity)
.showLoading(R.string.startLogin)
PttClient.getInstance().send("y\r\n".toByteArray(BIG5))
requireView().postDelayed(Runnable {
isWaitingForUserReply = false
}, 1000L)
}
.setNegativeButton("否") { _, _ ->
(requireActivity() as MainActivity)
.showLoading(R.string.startLogin)
PttClient.getInstance().send("n\r\n".toByteArray(BIG5))
requireView().postDelayed(Runnable {
isWaitingForUserReply = false
}, 1000L)
}
.setCancelable(false)
.show()
}
}
// ...
这边就是先前提到需让使用者回覆的状态,关闭loading状态後跳出AlertDialog让使用者选择是否要踢出站上的帐号。
使用者选择後再把Loading状态开启并把isWaitingForUserReply设回false,由於Ptt处理这个动作会花比较多的时间,所以我是设1秒後再呼叫expect。
同样的为了确保不会有重复跳出Dialog的状况,所以多设了一个hasAskedForMultiUsers的Flag判断。
// ...
3 -> { // "您要删除以上错误尝试的记录吗?"
if (hasSentNotDeleteRecord) continue
hasSentNotDeleteRecord = true
PttClient.getInstance().send("n\r\n".toByteArray(BIG5))
delay(1000L)
}
// ...
由於我并未显示尝试错误的内容出来,因此也不打算在我的App内把错误纪录删掉,於是直接回送"n"来保留纪录。
// ...
4 -> { // "【主功能表】"
withContext(Dispatchers.Main) {
(requireActivity() as MainActivity).dismissLoading()
NavHostFragment.findNavController(this@LoginFragment)
.navigate(R.id.action_loginFragment_to_searchArticleFragment)
}
break
}
// ...
确认登入成功後就能进到下一页,准备搜寻文章了!
<<: Day7 - 2D渲染环境基础篇 III[ 变形与阵列运算 ] - 成为Canvas Ninja ~ 理解2D渲染的精髓
>>: [Day07] TS:什麽是 Utility Types?
GenServer 跟我们游戏有什麽关西? 我来试试看直接套用我们的场景来解释 GenServer ...
重新认识 Vue.js | Kuro Hsu 2-2 元件之间的沟通传递 元件与自订事件 paren...
深度学习流程 我们知道深度学习是三个步骤,首先定义一个function set和structure,...
笔者对网站的认知为前端、後端及资料库,使用者在浏览器画面按下按钮或是送出表格,触发前端事件,将收集起...
至目前的章节为止,已经可以执行 Genero FGL的程序在 Windows/MAC/Linux ...