diff --git a/.idea/dictionaries/spayi.xml b/.idea/dictionaries/spayi.xml new file mode 100644 index 0000000..15b1e8c --- /dev/null +++ b/.idea/dictionaries/spayi.xml @@ -0,0 +1,7 @@ + + + + recv + + + \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index f7d993c..0b0226d 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -13,8 +13,9 @@ android { applicationId "top.fumiama.simpledict" minSdkVersion 26 targetSdkVersion 30 - versionCode 4 - versionName '1.2' + versionCode 5 + versionName '1.3' + resConfigs "zh", "zh-rCN" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } diff --git a/app/src/main/java/top/fumiama/simpledict/Client.kt b/app/src/main/java/top/fumiama/simpledict/Client.kt index 1ad560b..2c70b10 100644 --- a/app/src/main/java/top/fumiama/simpledict/Client.kt +++ b/app/src/main/java/top/fumiama/simpledict/Client.kt @@ -6,7 +6,7 @@ import java.io.InputStream import java.io.OutputStream import java.net.Socket -class Client(val ip: String, val port: Int) { +class Client(private val ip: String, private val port: Int) { //普通数据交互接口 private var sc: Socket? = null @@ -15,23 +15,22 @@ class Client(val ip: String, val port: Int) { private var din: InputStream? = null //已连接标记 - var isConnect = false + private val isConnect get() = sc != null && din != null && dout != null /** * 初始化普通交互连接 */ - fun initConnect(){ - try { + fun initConnect(depth: Int = 0){ + if(depth > 3) Log.d("MyC", "connect server failed after $depth tries") + else try { sc = Socket(ip, port) //通过socket连接服务器 din = sc?.getInputStream() //获取输入流并转换为StreamReader,约定编码格式 dout = sc?.getOutputStream() //获取输出流 - sc?.soTimeout = 10000 //设置连接超时限制 - if (sc != null && din != null && dout != null) { //判断一下是否都连上,避免NullPointException - isConnect = true - Log.d("MyC", "connect server successful") - } else { - Log.d("MyC", "connect server failed,now retry...") - initConnect() + sc?.soTimeout = 2333 //设置连接超时限制 + if (isConnect) Log.d("MyC", "connect server successful") + else { + Log.d("MyC", "connect server failed, now retry...") + initConnect(depth + 1) } } catch (e: IOException) { //获取输入输出流是可能报IOException的,所以必须try-catch e.printStackTrace() @@ -45,30 +44,31 @@ class Client(val ip: String, val port: Int) { fun sendMessage(message: CharSequence?) { try { if (isConnect) { - if (dout != null && message != null) { //判断输出流或者消息是否为空,为空的话会产生nullpoint错误 - dout!!.write(message.toString().toByteArray()) - dout!!.flush() - } else Log.d("MyC", "The message to be sent is empty or have no connect") + if (message != null) { //判断输出流或者消息是否为空,为空的话会产生nullpoint错误 + dout?.write(message.toString().toByteArray()) + dout?.flush() + Log.d("MyC", "Send msg: $message") + } else Log.d("MyC", "The message to be sent is empty") Log.d("MyC", "send message succeed") - } else Log.d("MyC", "no connect to send message") + } else Log.d("MyC", "send message failed: no connect") } catch (e: IOException) { - Log.d("MyC", "send message to cilent failed") + Log.d("MyC", "send message failed: crash") e.printStackTrace() } } - fun receiveRawMessage() : ByteArray { + fun receiveRawMessage(totalSize: Int = -1, bufferSize: Int = 4096) : ByteArray { var re = byteArrayOf() try { if (isConnect) { Log.d("MyC", "开始接收服务端信息") - val inMessage = ByteArray(4096) //设置接受缓冲,避免接受数据过长占用过多内存 + val inMessage = ByteArray(bufferSize) //设置接受缓冲,避免接受数据过长占用过多内存 var a: Int do { a = din?.read(inMessage)?:0 //a存储返回消息的长度 - Log.d("MyC", "reply length:$a: ${inMessage.decodeToString()}") re += inMessage.copyOf(a) - } while (a == 4096) + Log.d("MyC", "reply length:$a: ${re.decodeToString()}") + } while (a == bufferSize || totalSize > re.size) } else Log.d("MyC", "no connect to receive message") } catch (e: IOException) { Log.d("MyC", "receive message failed") @@ -87,10 +87,12 @@ class Client(val ip: String, val port: Int) { din?.close() dout?.close() sc?.close() + sc = null + din = null + dout = null } catch (e: IOException) { e.printStackTrace() } - isConnect = false Log.d("MyC", "关闭连接") } } diff --git a/app/src/main/java/top/fumiama/simpledict/MainActivity.kt b/app/src/main/java/top/fumiama/simpledict/MainActivity.kt index ad887a0..a05c9fd 100644 --- a/app/src/main/java/top/fumiama/simpledict/MainActivity.kt +++ b/app/src/main/java/top/fumiama/simpledict/MainActivity.kt @@ -9,10 +9,12 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.EditText +import android.widget.Toast import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AppCompatActivity import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView +import com.google.android.material.textfield.TextInputLayout import com.lapism.search.internal.SearchLayout import com.lapism.search.util.SearchUtils import kotlinx.android.synthetic.main.activity_main.* @@ -21,6 +23,14 @@ import kotlinx.android.synthetic.main.line_word.view.* class MainActivity : AppCompatActivity() { private val dict = SimpleDict(Client("127.0.0.1", 8000), "fumiama") private var hasLiked = false + private val fetchThread get() = Thread{ + dict.fetchDict { + runOnUiThread { + Toast.makeText(this@MainActivity, "刷新成功", Toast.LENGTH_SHORT).show() + ffsw.isRefreshing = false + } + } + } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -35,8 +45,10 @@ class MainActivity : AppCompatActivity() { ffsw.apply { setOnRefreshListener { ad.refresh() - isRefreshing = false + fetchThread.start() } + isRefreshing = true + fetchThread.start() } } @@ -107,15 +119,15 @@ class MainActivity : AppCompatActivity() { } private fun showDictAlert(key: String, data: String?) { - val like = getSharedPreferences("dict", MODE_PRIVATE)?.contains(key)?:false + val hintAdd = if(data != null && data != "null") "重设" else "添加" hasLiked = false AlertDialog.Builder(this@MainActivity) .setTitle(key) .setMessage(data) - .setPositiveButton(if(data != "null") "重设" else "添加") { _, _ -> + .setPositiveButton(hintAdd) { _, _ -> val t = EditText(this@MainActivity) AlertDialog.Builder(this@MainActivity) - .setTitle("重设$key") + .setTitle("$hintAdd$key") .setView(t) .setPositiveButton(android.R.string.ok) { _, _ -> if (t.text.isNotEmpty()) Thread { @@ -125,12 +137,8 @@ class MainActivity : AppCompatActivity() { .setNegativeButton(android.R.string.cancel) { _, _ -> } .show() } - .setNeutralButton(if(like) "取消收藏" else "收藏") { _, _ -> - getSharedPreferences("dict", MODE_PRIVATE)?.edit()?.apply { - if(like) remove(key) else putString(key, data) - hasLiked = true - apply() - } + .setNeutralButton("删除") { _, _ -> + Thread{dict -= key}.start() } .setNegativeButton(android.R.string.cancel) { _, _ -> } .show() @@ -182,19 +190,38 @@ class MainActivity : AppCompatActivity() { @SuppressLint("ClickableViewAccessibility", "SetTextI18n") override fun onBindViewHolder(holder: ListViewHolder, position: Int) { Log.d("MyMain", "Bind like at $position") - listKeys?.apply { - if (position < size) { - val key = get(position) - val data = getValue(key) - holder.itemView.apply { - ta.text = key - tb.text = data - setOnClickListener { - showDictAlert(key, data) + Thread{ + listKeys?.apply { + if (position < size) { + val key = get(position) + val data = getValue(key) + val like = getSharedPreferences("dict", MODE_PRIVATE)?.contains(key) == true + runOnUiThread { + holder.itemView.apply { + ta.text = key + tb.text = data + if(like) vl.setBackgroundResource(R.drawable.ic_like_filled) + setOnClickListener { + showDictAlert(key, data) + } + vl.setOnClickListener { + getSharedPreferences("dict", MODE_PRIVATE)?.edit()?.apply { + if(like) { + remove(key) + it.setBackgroundResource(R.drawable.ic_like) + } else { + putString(key, data) + it.setBackgroundResource(R.drawable.ic_like_filled) + } + hasLiked = true + apply() + } + } + } } } } - } + }.start() } override fun getItemCount() = listKeys?.size?:0 diff --git a/app/src/main/java/top/fumiama/simpledict/SimpleDict.kt b/app/src/main/java/top/fumiama/simpledict/SimpleDict.kt index 8cb9315..54fe9f7 100644 --- a/app/src/main/java/top/fumiama/simpledict/SimpleDict.kt +++ b/app/src/main/java/top/fumiama/simpledict/SimpleDict.kt @@ -10,21 +10,44 @@ class SimpleDict(private val client: Client, private val pwd: String) { //must //val size get() = dict.size private val raw: ByteArray get() { - initDict() - client.sendMessage("cat") - sleep(2333) - val re = client.receiveRawMessage() - closeDict() + var times = 3 + var re: ByteArray + var firstRecv: ByteArray + do { + re = byteArrayOf() + initDict() + sendMessageWithDelay("cat", 2333) + try { + firstRecv = client.receiveRawMessage() + val firstStr = firstRecv.decodeToString() + var length = "" + for ((i, c) in firstStr.withIndex()) { + if(c.isDigit()) length += c + else { + if(i + 1 < firstRecv.size) re = firstRecv.copyOfRange(i, firstRecv.size) + break + } + } + re += client.receiveRawMessage(length.toInt() - re.size) + break + } catch (e: Exception){ + e.printStackTrace() + } + closeDict() + } while (times-- > 0) return re } - - init { - Thread{ fetchDict() }.start() - } + + private fun sendMessageWithDelay(msg: CharSequence, delay: Long = 233) = Thread{ + client.sendMessage(msg) + sleep(delay) + }.start() private fun initDict() { client.initConnect() client.sendMessage(pwd) + client.receiveRawMessage() + sleep(233) } private fun closeDict() { @@ -45,59 +68,36 @@ class SimpleDict(private val client: Client, private val pwd: String) { //must //fun filterKeys(predicate: (String) -> Boolean) = dict.filterKeys(predicate) fun filterValues(predicate: (String?) -> Boolean) = dict.filterValues(predicate) - fun fetchDict() { + fun fetchDict(doOnLoadSuccess: ()->Unit = { + Log.d("MySD", "Fetch dict success") + }) { val dictBlock = ByteArray(128) + dict = hashMapOf() raw.inputStream().let { - var c = '1' - while (!it.read().toChar().isDigit()) Log.d("MySD", "Skip banner.") - while (c.isDigit()) { - c = it.read().toChar() - Log.d("MySD", "Skip digit $c.") - } - dictBlock[0] = c.toByte() - if(it.read(dictBlock, 1, 127) == 127) { - analyzeDictBlk(dictBlock) - while (it.read(dictBlock, 0, 128) == 128) analyzeDictBlk(dictBlock) - } + while (it.read(dictBlock, 0, 128) == 128) analyzeDictBlk(dictBlock) + doOnLoadSuccess() } } - /*fun keysWithPattern(pattern: String): MutableSet{ - val re = mutableSetOf() + operator fun minusAssign(key: String) { initDict() - client.sendMessage("lst") - sleep(233) + sendMessageWithDelay("del") + client.receiveMessage() + sendMessageWithDelay(key) client.receiveMessage() - client.sendMessage(pattern) - client.receiveMessage()?.substringBeforeLast('\n')?.split('\n')?.forEach { - re.add(it) - } closeDict() - return re } - fun getDirectly(key: String): String? { - initDict() - client.sendMessage("get") - sleep(233) - client.receiveMessage() - client.sendMessage(key) - val re = client.receiveMessage() - closeDict() - return re - }*/ - operator fun get(key: String) = dict[key] operator fun set(key: String, value: String): String? { val p = dict[key] initDict() - client.sendMessage("set") - sleep(233) + sendMessageWithDelay("set") client.receiveMessage() - client.sendMessage(key) + sendMessageWithDelay(key) client.receiveMessage() - client.sendMessage(value) + sendMessageWithDelay(value) client.receiveMessage() closeDict() dict[key] = value diff --git a/app/src/main/res/drawable-anydpi/ic_like.xml b/app/src/main/res/drawable-anydpi/ic_like.xml new file mode 100644 index 0000000..87c647d --- /dev/null +++ b/app/src/main/res/drawable-anydpi/ic_like.xml @@ -0,0 +1,10 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable-anydpi/ic_like_filled.xml b/app/src/main/res/drawable-anydpi/ic_like_filled.xml new file mode 100644 index 0000000..a4344c9 --- /dev/null +++ b/app/src/main/res/drawable-anydpi/ic_like_filled.xml @@ -0,0 +1,10 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index b7fc59a..fa4202f 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -1,32 +1,33 @@ - - app:layout_scrollFlags="scroll" - app:toolbarId="@+id/toolbar"> - - - + android:layout_height="match_parent" + app:contentScrim="?attr/colorSurface" + app:toolbarId="@+id/toolbar"> + + + + - - \ No newline at end of file diff --git a/app/src/main/res/layout/line_word.xml b/app/src/main/res/layout/line_word.xml index 2f3b9bd..c0d5939 100644 --- a/app/src/main/res/layout/line_word.xml +++ b/app/src/main/res/layout/line_word.xml @@ -1,31 +1,50 @@ - + android:foreground="?android:attr/selectableItemBackground"> - + android:background="@drawable/ic_like" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintTop_toTopOf="parent" + android:clickable="true" + android:focusable="true" + android:foreground="?android:attr/selectableItemBackground"/> - - \ No newline at end of file + android:layout_height="match_parent" + android:orientation="vertical"> + + + + + + + + diff --git a/app/src/main/res/values-night/themes.xml b/app/src/main/res/values-night/themes.xml index 5b24d00..517f8e3 100644 --- a/app/src/main/res/values-night/themes.xml +++ b/app/src/main/res/values-night/themes.xml @@ -4,11 +4,11 @@ @color/purple_200 @color/purple_700 - @color/black + @android:color/black @color/teal_200 @color/teal_200 - @color/black + @android:color/black ?attr/colorPrimaryVariant diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index 699a436..37fc22d 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -1,12 +1,8 @@ #FFBB86FC - #FF6200EE #FF3700B3 #FF03DAC5 - #FF018786 - #FF000000 - #FFFFFFFF #BA8D08 #FF9800 #FF845E diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml deleted file mode 100644 index 125df87..0000000 --- a/app/src/main/res/values/dimens.xml +++ /dev/null @@ -1,3 +0,0 @@ - - 16dp - \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 87f9780..32425cb 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1,12 +1,3 @@ SimpleDict - Settings - - First Fragment - Second Fragment - Next - Previous - - Hello first fragment - Hello second fragment. Arg: %1$s \ No newline at end of file diff --git a/app/winrelease/app-winrelease.apk b/app/winrelease/app-winrelease.apk index 55c24c1..a8e5cf4 100644 Binary files a/app/winrelease/app-winrelease.apk and b/app/winrelease/app-winrelease.apk differ diff --git a/app/winrelease/output-metadata.json b/app/winrelease/output-metadata.json index 8f98f00..fbc96a3 100644 --- a/app/winrelease/output-metadata.json +++ b/app/winrelease/output-metadata.json @@ -10,8 +10,8 @@ { "type": "SINGLE", "filters": [], - "versionCode": 4, - "versionName": "1.2", + "versionCode": 5, + "versionName": "1.3", "outputFile": "app-winrelease.apk" } ]