mirror of
https://github.com/fumiama/simple-dict-android.git
synced 2026-06-12 14:10:26 +08:00
v3.0
1. 适配md5 2. 优化client
This commit is contained in:
3
.idea/dictionaries/rumia.xml
generated
3
.idea/dictionaries/rumia.xml
generated
@@ -1,6 +1,9 @@
|
|||||||
<component name="ProjectDictionaryState">
|
<component name="ProjectDictionaryState">
|
||||||
<dictionary name="rumia">
|
<dictionary name="rumia">
|
||||||
<words>
|
<words>
|
||||||
|
<w>datas</w>
|
||||||
|
<w>fumiama</w>
|
||||||
|
<w>nequ</w>
|
||||||
<w>slle</w>
|
<w>slle</w>
|
||||||
<w>spwd</w>
|
<w>spwd</w>
|
||||||
</words>
|
</words>
|
||||||
|
|||||||
@@ -12,8 +12,8 @@ android {
|
|||||||
applicationId "top.fumiama.simpledict"
|
applicationId "top.fumiama.simpledict"
|
||||||
minSdkVersion 26
|
minSdkVersion 26
|
||||||
targetSdkVersion 30
|
targetSdkVersion 30
|
||||||
versionCode 16
|
versionCode 17
|
||||||
versionName '2.1'
|
versionName '3.0'
|
||||||
resConfigs "zh", "zh-rCN"
|
resConfigs "zh", "zh-rCN"
|
||||||
|
|
||||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||||
|
|||||||
27
app/src/main/java/top/fumiama/simpledict/ByteArrayQueue.kt
Normal file
27
app/src/main/java/top/fumiama/simpledict/ByteArrayQueue.kt
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
package top.fumiama.simpledict
|
||||||
|
//Fumiama 20210601
|
||||||
|
//ByteArrayQueue.kt
|
||||||
|
//FIFO队列
|
||||||
|
class ByteArrayQueue {
|
||||||
|
private var elements = byteArrayOf()
|
||||||
|
val size get() = elements.size
|
||||||
|
fun append(items: ByteArray) {
|
||||||
|
elements += items
|
||||||
|
}
|
||||||
|
fun pop(num: Int = 1): ByteArray? {
|
||||||
|
return if(num <= elements.size) {
|
||||||
|
val re = elements.copyOfRange(0, num)
|
||||||
|
elements = elements.copyOfRange(num, elements.size)
|
||||||
|
re
|
||||||
|
} else null
|
||||||
|
}
|
||||||
|
fun clear() {
|
||||||
|
elements = byteArrayOf()
|
||||||
|
}
|
||||||
|
fun popAll(): ByteArray {
|
||||||
|
val re = elements
|
||||||
|
clear()
|
||||||
|
return re
|
||||||
|
}
|
||||||
|
operator fun plusAssign(items: ByteArray) = append(items)
|
||||||
|
}
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
package top.fumiama.simpledict
|
package top.fumiama.simpledict
|
||||||
|
//Fumiama 20210601
|
||||||
|
//Client.kt
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import java.io.IOException
|
import java.io.*
|
||||||
import java.io.InputStream
|
import java.lang.Thread.sleep
|
||||||
import java.io.OutputStream
|
|
||||||
import java.net.Socket
|
import java.net.Socket
|
||||||
|
|
||||||
class Client(private val ip: String, private val port: Int) {
|
class Client(private val ip: String, private val port: Int) {
|
||||||
@@ -26,7 +26,7 @@ class Client(private val ip: String, private val port: Int) {
|
|||||||
sc = Socket(ip, port) //通过socket连接服务器
|
sc = Socket(ip, port) //通过socket连接服务器
|
||||||
din = sc?.getInputStream() //获取输入流并转换为StreamReader,约定编码格式
|
din = sc?.getInputStream() //获取输入流并转换为StreamReader,约定编码格式
|
||||||
dout = sc?.getOutputStream() //获取输出流
|
dout = sc?.getOutputStream() //获取输出流
|
||||||
sc?.soTimeout = 2333 //设置连接超时限制
|
sc?.soTimeout = 10000 //设置连接超时限制
|
||||||
return if (isConnect) {
|
return if (isConnect) {
|
||||||
Log.d("MyC", "connect server successful")
|
Log.d("MyC", "connect server successful")
|
||||||
true
|
true
|
||||||
@@ -44,13 +44,15 @@ class Client(private val ip: String, private val port: Int) {
|
|||||||
* 发送数据至服务器
|
* 发送数据至服务器
|
||||||
* @param message 要发送至服务器的字符串
|
* @param message 要发送至服务器的字符串
|
||||||
*/
|
*/
|
||||||
fun sendMessage(message: CharSequence?): Boolean {
|
fun sendMessage(message: String?): Boolean = sendMessage(message?.toByteArray())
|
||||||
|
|
||||||
|
fun sendMessage(message: ByteArray?): Boolean {
|
||||||
try {
|
try {
|
||||||
if (isConnect) {
|
if (isConnect) {
|
||||||
if (message != null) { //判断输出流或者消息是否为空,为空的话会产生nullpoint错误
|
if (message != null) { //判断输出流或者消息是否为空,为空的话会产生null pointer错误
|
||||||
dout?.write(message.toString().toByteArray())
|
dout?.write(message)
|
||||||
dout?.flush()
|
dout?.flush()
|
||||||
Log.d("MyC", "Send msg: $message")
|
Log.d("MyC", "Send msg: ${message.decodeToString()}")
|
||||||
return true
|
return true
|
||||||
} else Log.d("MyC", "The message to be sent is empty")
|
} else Log.d("MyC", "The message to be sent is empty")
|
||||||
Log.d("MyC", "send message succeed")
|
Log.d("MyC", "send message succeed")
|
||||||
@@ -62,58 +64,35 @@ class Client(private val ip: String, private val port: Int) {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
var buffer = byteArrayOf()
|
fun read(): Char? = din?.read()?.toChar()
|
||||||
|
|
||||||
fun receiveRawMessage(totalSize: Int = -1, bufferSize: Int = 1048576, setProgress: Boolean = false) : ByteArray {
|
private var buffer = ByteArrayQueue()
|
||||||
if(totalSize == buffer.size) {
|
private val receiveBuffer = ByteArray(65536)
|
||||||
val re = buffer
|
|
||||||
buffer = byteArrayOf()
|
fun receiveRawMessage(totalSize: Int, setProgress: Boolean = false) : ByteArray {
|
||||||
return re
|
if(totalSize == buffer.size) return buffer.popAll()
|
||||||
} else {
|
else {
|
||||||
var re = byteArrayOf()
|
|
||||||
try {
|
try {
|
||||||
if (isConnect) {
|
if (isConnect) {
|
||||||
Log.d("MyC", "开始接收服务端信息")
|
Log.d("MyC", "开始接收服务端信息")
|
||||||
val inMessage = ByteArray(bufferSize) //设置接受缓冲,避免接受数据过长占用过多内存
|
while(totalSize > buffer.size) {
|
||||||
var a: Int
|
val count = din?.read(receiveBuffer)?:0
|
||||||
do {
|
if(count > 0) {
|
||||||
a = din?.read(inMessage)?:0 //a存储返回消息的长度
|
buffer += receiveBuffer.copyOfRange(0, count)
|
||||||
if(a > 0) {
|
Log.d("MyC", "reply length:$count")
|
||||||
re += inMessage.copyOf(a)
|
if(setProgress && totalSize > 0) progress?.notify(100 * buffer.size / totalSize)
|
||||||
Log.d("MyC", "reply length:$a")
|
} else sleep(10)
|
||||||
if(totalSize < 0 && a < bufferSize) break
|
}
|
||||||
else if(setProgress && totalSize > 0) progress?.notify(100 * re.size / totalSize)
|
|
||||||
} else break
|
|
||||||
} while (totalSize > re.size)
|
|
||||||
} else Log.d("MyC", "no connect to receive message")
|
} else Log.d("MyC", "no connect to receive message")
|
||||||
} catch (e: IOException) {
|
} catch (e: IOException) {
|
||||||
Log.d("MyC", "receive message failed")
|
Log.d("MyC", "receive message failed")
|
||||||
e.printStackTrace()
|
e.printStackTrace()
|
||||||
}
|
}
|
||||||
if(totalSize > 0 && re.size > totalSize) {
|
return if(totalSize > 0) buffer.pop(totalSize)?:byteArrayOf() else buffer.popAll()
|
||||||
Log.d("MyC", "Reduce re size from ${re.size} to $totalSize")
|
|
||||||
buffer += re.copyOfRange(totalSize, re.size)
|
|
||||||
re = re.copyOf(totalSize)
|
|
||||||
} else if(totalSize > 0 && buffer.isNotEmpty()) {
|
|
||||||
Log.d("MyC", "Increase re size.")
|
|
||||||
buffer += re
|
|
||||||
if(buffer.size > totalSize) {
|
|
||||||
re = buffer.copyOf(totalSize)
|
|
||||||
buffer = buffer.copyOfRange(totalSize, buffer.size)
|
|
||||||
} else {
|
|
||||||
re = buffer
|
|
||||||
buffer = byteArrayOf()
|
|
||||||
}
|
|
||||||
} else if(totalSize < 0 && buffer.isNotEmpty()) {
|
|
||||||
re = buffer
|
|
||||||
buffer = byteArrayOf()
|
|
||||||
Log.d("MyC", "clear buffer")
|
|
||||||
}
|
|
||||||
return re
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun receiveMessage(totalSize: Int = -1) = receiveRawMessage(totalSize).decodeToString()
|
fun receiveMessage(totalSize: Int) = receiveRawMessage(totalSize).decodeToString()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 关闭连接
|
* 关闭连接
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ import kotlinx.android.synthetic.main.dialog_input.view.*
|
|||||||
import kotlinx.android.synthetic.main.line_word.view.*
|
import kotlinx.android.synthetic.main.line_word.view.*
|
||||||
import kotlinx.android.synthetic.main.line_word.view.tb
|
import kotlinx.android.synthetic.main.line_word.view.tb
|
||||||
import kotlinx.android.synthetic.main.line_word.view.tn
|
import kotlinx.android.synthetic.main.line_word.view.tn
|
||||||
|
import java.io.File
|
||||||
import java.io.FileNotFoundException
|
import java.io.FileNotFoundException
|
||||||
import java.lang.Exception
|
import java.lang.Exception
|
||||||
|
|
||||||
@@ -55,7 +56,7 @@ class MainActivity : AppCompatActivity() {
|
|||||||
if(contains("pwd")) getString("pwd", pwd)?.apply { pwd = this }
|
if(contains("pwd")) getString("pwd", pwd)?.apply { pwd = this }
|
||||||
if(contains("spwd")) getString("spwd", spwd)?.apply { spwd = this }
|
if(contains("spwd")) getString("spwd", spwd)?.apply { spwd = this }
|
||||||
}
|
}
|
||||||
dict = SimpleDict(Client(host, port), pwd, spwd)
|
dict = SimpleDict(Client(host, port), pwd, externalCacheDir, spwd)
|
||||||
ad = LikeViewHolder(ffr).RecyclerViewAdapter()
|
ad = LikeViewHolder(ffr).RecyclerViewAdapter()
|
||||||
cm = getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
|
cm = getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
|
||||||
ffr.apply {
|
ffr.apply {
|
||||||
@@ -68,7 +69,7 @@ class MainActivity : AppCompatActivity() {
|
|||||||
|
|
||||||
ffsw.apply {
|
ffsw.apply {
|
||||||
setOnRefreshListener {
|
setOnRefreshListener {
|
||||||
fetchThread{
|
fetchThread {
|
||||||
updateSize()
|
updateSize()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,84 +1,99 @@
|
|||||||
package top.fumiama.simpledict
|
package top.fumiama.simpledict
|
||||||
|
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
|
import java.io.File
|
||||||
|
import java.lang.Thread.sleep
|
||||||
|
import java.security.MessageDigest
|
||||||
|
|
||||||
class SimpleDict(private val client: Client, private val pwd: String, private val spwd: String?) { //must run in thread
|
class SimpleDict(private val client: Client, private val pwd: String, private val externalCacheDir: File?, private val spwd: String?) { //must run in thread
|
||||||
private var dict = HashMap<String, String?>()
|
private var dict = HashMap<String, String?>()
|
||||||
val size get() = dict.size
|
val size get() = dict.size
|
||||||
val keys get() = dict.keys
|
val keys get() = dict.keys
|
||||||
var latestKeys = arrayOf<String>()
|
var latestKeys = arrayOf<String>()
|
||||||
|
private val md5File = File(externalCacheDir, "md5")
|
||||||
|
private val dspFile = File(externalCacheDir, "dsp")
|
||||||
private val raw: ByteArray?
|
private val raw: ByteArray?
|
||||||
get() {
|
get() {
|
||||||
var times = 3
|
var times = 3
|
||||||
var re: ByteArray
|
var re: ByteArray? = null
|
||||||
var firstRecv: ByteArray
|
var exit = false
|
||||||
do {
|
while(times-- > 0 && !exit) {
|
||||||
re = byteArrayOf()
|
|
||||||
if(initDict()) {
|
if(initDict()) {
|
||||||
sendMessage("cat")
|
client.sendMessage("cat")
|
||||||
try {
|
try {
|
||||||
firstRecv = client.receiveRawMessage()
|
|
||||||
val firstStr = firstRecv.decodeToString()
|
|
||||||
var length = ""
|
var length = ""
|
||||||
Log.d("MySD", "first str: $firstStr")
|
var c = client.read()
|
||||||
for ((i, c) in firstStr.withIndex()) {
|
while (c?.isDigit() == true) {
|
||||||
if(c.isDigit()) length += c
|
length += c
|
||||||
else {
|
c = client.read()
|
||||||
if(i + 2 < firstRecv.size) re = firstRecv.copyOfRange(i + 1, firstRecv.size)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Log.d("MySD", "length: $length")
|
Log.d("MySD", "length: $length")
|
||||||
re += client.receiveRawMessage(length.toInt() - re.size)
|
re = client.receiveRawMessage(length.toInt())
|
||||||
closeDict()
|
exit = true
|
||||||
break
|
|
||||||
} catch (e: Exception){
|
} catch (e: Exception){
|
||||||
e.printStackTrace()
|
e.printStackTrace()
|
||||||
closeDict()
|
|
||||||
}
|
}
|
||||||
}
|
closeDict()
|
||||||
} while (times-- > 0)
|
} else sleep(233)
|
||||||
return if(re.isEmpty()) null else re
|
}
|
||||||
|
return re
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun sendMessage(msg: CharSequence) = client.sendMessage(msg)
|
|
||||||
|
|
||||||
private fun initDict(): Boolean {
|
private fun initDict(): Boolean {
|
||||||
if(client.initConnect()){
|
if(client.initConnect()){
|
||||||
if(client.sendMessage(pwd)) {
|
if(client.sendMessage(pwd)) {
|
||||||
client.receiveRawMessage(31)
|
return client.receiveRawMessage(31).size == 31
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun closeDict(): Boolean {
|
private fun closeDict(): Boolean {
|
||||||
if(client.sendMessage("quit")) {
|
client.sendMessage("quit")
|
||||||
if (client.closeConnect()) return true
|
return client.closeConnect()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun saveDict(data: ByteArray) {
|
||||||
|
if(externalCacheDir?.exists() != true) externalCacheDir?.mkdirs()
|
||||||
|
if(externalCacheDir?.exists() == true) {
|
||||||
|
dspFile.writeBytes(data)
|
||||||
|
md5File.writeBytes(MessageDigest.getInstance("md5").digest(data))
|
||||||
}
|
}
|
||||||
return false
|
}
|
||||||
|
|
||||||
|
private fun hasNewItem(md5: ByteArray): Boolean =
|
||||||
|
if(initDict()) {
|
||||||
|
client.sendMessage("md5".toByteArray() + md5)
|
||||||
|
client.receiveRawMessage(3) //md5
|
||||||
|
val re = client.receiveMessage(4)
|
||||||
|
closeDict()
|
||||||
|
Log.d("MySD", "Check md5: $re")
|
||||||
|
re == "nequ"
|
||||||
|
} else false
|
||||||
|
|
||||||
|
private fun analyzeDict(datas: ByteArray, saveDict: Boolean) {
|
||||||
|
SimpleProtobuf.getDictArray(datas).forEach { d ->
|
||||||
|
d?.apply {
|
||||||
|
val k = key.decodeToString()
|
||||||
|
dict[k] = data.decodeToString()
|
||||||
|
latestKeys += k
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(saveDict) saveDict(datas)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun filterValues(predicate: (String?) -> Boolean) = dict.filterValues(predicate)
|
fun filterValues(predicate: (String?) -> Boolean) = dict.filterValues(predicate)
|
||||||
|
|
||||||
fun fetchDict(doOnLoadFailure: ()->Unit = {
|
fun fetchDict(doOnLoadFailure: ()->Unit, doOnLoadSuccess: ()->Unit, doCommon: (() -> Unit)? = null) {
|
||||||
Log.d("MySD", "Fetch dict success")
|
|
||||||
}, doOnLoadSuccess: ()->Unit = {
|
|
||||||
Log.d("MySD", "Fetch dict success")
|
|
||||||
}, doCommon: (() -> Unit)? = null) {
|
|
||||||
dict = hashMapOf()
|
dict = hashMapOf()
|
||||||
latestKeys = arrayOf()
|
latestKeys = arrayOf()
|
||||||
raw?.let {
|
val noChange = md5File.exists() && dspFile.exists() && !hasNewItem(md5File.readBytes())
|
||||||
SimpleProtobuf.getDictArray(it).forEach { d ->
|
val data = if(noChange) dspFile.readBytes() else raw
|
||||||
d?.apply {
|
if(data == null) doOnLoadFailure()
|
||||||
val k = key.decodeToString()
|
else {
|
||||||
dict[k] = data.decodeToString()
|
analyzeDict(data, !noChange)
|
||||||
latestKeys += k
|
|
||||||
}
|
|
||||||
}
|
|
||||||
doOnLoadSuccess()
|
doOnLoadSuccess()
|
||||||
}?:doOnLoadFailure()
|
}
|
||||||
doCommon?.let { it() }
|
doCommon?.let { it() }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -86,9 +101,9 @@ class SimpleDict(private val client: Client, private val pwd: String, private va
|
|||||||
if(spwd == null) return false
|
if(spwd == null) return false
|
||||||
else if(initDict()) {
|
else if(initDict()) {
|
||||||
val delPass = "del$spwd"
|
val delPass = "del$spwd"
|
||||||
sendMessage(delPass)
|
client.sendMessage(delPass)
|
||||||
client.receiveRawMessage(delPass.length)
|
client.receiveRawMessage(delPass.length)
|
||||||
sendMessage(key)
|
client.sendMessage(key)
|
||||||
if(client.receiveMessage(4) == "succ") {
|
if(client.receiveMessage(4) == "succ") {
|
||||||
if(closeDict()) {
|
if(closeDict()) {
|
||||||
dict.remove(key)
|
dict.remove(key)
|
||||||
@@ -115,11 +130,11 @@ class SimpleDict(private val client: Client, private val pwd: String, private va
|
|||||||
if((contain && del(key)) || !contain) {
|
if((contain && del(key)) || !contain) {
|
||||||
if(initDict()) {
|
if(initDict()) {
|
||||||
val setPass = "set$spwd"
|
val setPass = "set$spwd"
|
||||||
sendMessage(setPass)
|
client.sendMessage(setPass)
|
||||||
client.receiveRawMessage(setPass.length)
|
client.receiveRawMessage(setPass.length)
|
||||||
sendMessage(key)
|
client.sendMessage(key)
|
||||||
if(client.receiveMessage(4) == "data") {
|
if(client.receiveMessage(4) == "data") {
|
||||||
sendMessage(value)
|
client.sendMessage(value)
|
||||||
client.receiveMessage(4)
|
client.receiveMessage(4)
|
||||||
if(closeDict()) dict[key] = value
|
if(closeDict()) dict[key] = value
|
||||||
return true
|
return true
|
||||||
|
|||||||
@@ -1,9 +1,6 @@
|
|||||||
package top.fumiama.simpledict;
|
package top.fumiama.simpledict;
|
||||||
|
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import java.util.Stack;
|
import java.util.Stack;
|
||||||
|
|
||||||
public class SimpleProtobuf {
|
public class SimpleProtobuf {
|
||||||
@@ -21,7 +18,7 @@ public class SimpleProtobuf {
|
|||||||
offset += getSLLE(raw, offset).len; //struct_len
|
offset += getSLLE(raw, offset).len; //struct_len
|
||||||
offset += getSLLE(raw, offset).len; //type
|
offset += getSLLE(raw, offset).len; //type
|
||||||
s = getSLLE(raw, offset); //data len
|
s = getSLLE(raw, offset); //data len
|
||||||
Log.d("MySPB", "Data len:" + s.value);
|
//Log.d("MySPB", "Data len:" + s.value);
|
||||||
Dict d = new Dict();
|
Dict d = new Dict();
|
||||||
d.key = new byte[s.value];
|
d.key = new byte[s.value];
|
||||||
offset += s.len;
|
offset += s.len;
|
||||||
@@ -29,7 +26,7 @@ public class SimpleProtobuf {
|
|||||||
offset += s.value;
|
offset += s.value;
|
||||||
offset += getSLLE(raw, offset).len; //type
|
offset += getSLLE(raw, offset).len; //type
|
||||||
s = getSLLE(raw, offset); //data len
|
s = getSLLE(raw, offset); //data len
|
||||||
Log.d("MySPB", "Data len:" + s.value);
|
//Log.d("MySPB", "Data len:" + s.value);
|
||||||
d.data = new byte[s.value];
|
d.data = new byte[s.value];
|
||||||
offset += s.len;
|
offset += s.len;
|
||||||
System.arraycopy(raw, offset, d.data, 0, s.value);
|
System.arraycopy(raw, offset, d.data, 0, s.value);
|
||||||
|
|||||||
Reference in New Issue
Block a user