今天來處理登入的流程。
送出登入的方式很簡單,使用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
}
// ...
確認登入成功後就能進到下一頁,準備搜尋文章了!