今天來做搜尋看板的部分,首先Layout我先簡單的放一個EditText以及Button,點擊Button後將輸入的內容送出做搜尋:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".SearchArticleFragment">
<EditText
android:id="@+id/searchBoardInput"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/one_and_half_grid_unit"
android:layout_marginTop="@dimen/one_and_half_grid_unit"
android:layout_marginEnd="@dimen/half_grid_unit"
android:background="@color/text_normal"
android:padding="@dimen/one_grid_unit"
android:textColor="@color/background"
app:layout_constraintEnd_toStartOf="@id/searchBoard"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="Gossip" />
<ImageView
android:id="@+id/searchBoard"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@color/text_normal"
android:padding="@dimen/half_grid_unit"
android:layout_marginEnd="@dimen/one_and_half_grid_unit"
android:src="@drawable/ic_baseline_location_searching"
app:layout_constraintBottom_toBottomOf="@id/searchBoardInput"
app:layout_constraintEnd_toEndOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
searchBoard.setOnClickListener {
val board = searchBoardInput.text.toString().trim()
searchBoardInput.setText(board)
if (board.isEmpty()) return@setOnClickListener
PttClient.getInstance().send("s$board ")
//...
}
搜尋的起始是小寫的英文字母s,接著就是輸入板名+空白鍵。以輸入"a"為例,在Ptt按上述操作輸入後的畫面如下:
接著要做的事便是將回傳畫面下方的看板列表給解析出來。
首先可以看到上圖的最後一行顯示"按空白鍵可列出更多項目",這代表以"a"搜尋還有更多的結果無法在一個畫面內呈現。若持續點擊空白鍵把結果走到最後的話,畫面的呈現如下:
唯一的差別就只有最後一行消失了。
於是為了確保解析出所有搜尋結果頁面,我先設定了以下Pattern:
private val searchBoardPattern = arrayOf(
"按空白鍵可列出更多項目",
"相關資訊一覽表"
)
如果先匹配到"按空白鍵可列出更多項目",代表還有內容需要解析,反之代表最後一頁。
viewLifecycleOwner.lifecycleScope.launch(Dispatchers.IO) {
val boardList = mutableListOf<String>()
do {
val ret = PttClient.getInstance().expect(searchBoardPattern)
Log.d(mTag, "expect: ret=$ret")
if (ret == 0 || ret == 1) {
// ... -> 解析看板名稱
}
if (ret == 0) {
PttClient.getInstance().send(" ")
delay(100L)
}
} while (ret == 0)
// ... -> 回歸主頁面
}
接下來要做的是上面程式碼註解中的解析看板名稱部分,解析出來的內容我會放到上方已宣告的boardList中。一樣我們先看畫面內容:
基本上要做的事情就如上圖的標記,拿到頁面的字串後分割出第3~22行的內容,並且將每行內容以長度22做分割。
val searchResult = PttClient.getInstance().getScreen().split("\n")
for (i in 3..22) {
val splitResult = arrayOf(
searchResult[i].substring(0, 22).trim(),
searchResult[i].substring(22, 44).trim(),
searchResult[i].substring(44).trim()
)
splitResult.forEach {
if (it.isNotEmpty()) {
boardList.add(it)
}
}
}
trim完之後不為空的內容就加入到boardList之中,以前面的"a"來搜尋的話,下中斷點能看到以下的結果:
在搜尋完成後我預計要做的事是將結果呈現並讓使用者做選擇,在此之前我想要先確保我的Ptt頁面是在主列表中,這樣在後續的操作會比較方便。而要離開搜尋的話需要送出Enter,但有時候當我搜尋的內容本來就有能匹配到的看板的話,按下Enter可能會直接進入該看板中,如下:
此時點擊Enter會直接進入該看板:
並且有些看板會有進版畫面:
因此在送出Enter後我會有以下Pattern需要判斷:
private val cancelSearchBoardPattern = arrayOf(
"【主功能表】",
"請按任意鍵繼續", // 進板畫面
"文章選讀", // 看板內
)
最後就簡單了。
PttClient.getInstance().send("\r\n")
var ret = PttClient.getInstance().expect(cancelSearchBoardPattern)
while (ret != 0 && ret != -1) {
PttClient.getInstance().send("qq")
delay(50L)
ret = PttClient.getInstance().expect(cancelSearchBoardPattern)
}
// PttClient.getInstance().printScreen()
一次送兩個q是為了能更快速的回到主列表。
因為不想每次送出內容都還要另外加toByteArray(Charset)來轉換成big5編碼的byte array,所以我有複寫了PttClient的send方法,之後直接送字串就好了。
override fun send(text: String?) {
text?.run {
send(toByteArray(BIG5))
}
}
今天的內容就到這邊了,明天是想把搜尋的結果呈現給User做點選,應該內容會比較少一點。