diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..aa724b7 --- /dev/null +++ b/.gitignore @@ -0,0 +1,15 @@ +*.iml +.gradle +/local.properties +/.idea/caches +/.idea/libraries +/.idea/modules.xml +/.idea/workspace.xml +/.idea/navEditor.xml +/.idea/assetWizardSettings.xml +.DS_Store +/build +/captures +.externalNativeBuild +.cxx +local.properties diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..26d3352 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,3 @@ +# Default ignored files +/shelf/ +/workspace.xml diff --git a/.idea/compiler.xml b/.idea/compiler.xml new file mode 100644 index 0000000..61a9130 --- /dev/null +++ b/.idea/compiler.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/gradle.xml b/.idea/gradle.xml new file mode 100644 index 0000000..23a89bb --- /dev/null +++ b/.idea/gradle.xml @@ -0,0 +1,22 @@ + + + + + + + \ No newline at end of file diff --git a/.idea/jarRepositories.xml b/.idea/jarRepositories.xml new file mode 100644 index 0000000..a5f05cd --- /dev/null +++ b/.idea/jarRepositories.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..d5d35ec --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,9 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/app/.gitignore b/app/.gitignore new file mode 100644 index 0000000..42afabf --- /dev/null +++ b/app/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle new file mode 100644 index 0000000..230aa03 --- /dev/null +++ b/app/build.gradle @@ -0,0 +1,109 @@ +plugins { + id 'com.android.application' + id 'kotlin-android' + id 'kotlin-android-extensions' + id 'AndResGuard' +} + +android { + compileSdkVersion 30 + buildToolsVersion "30.0.2" + + defaultConfig { + applicationId "top.fumiama.simpledict" + minSdkVersion 23 + targetSdkVersion 30 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + + signingConfigs { + winrelease { + storeFile file('C:/Users/spayi/OneDrive/swc/developer/android_key/open_key') + storePassword 'fumiama' + keyAlias 'default' + keyPassword 'fumiama' + v1SigningEnabled true + v2SigningEnabled true + } + } + + buildTypes { + winrelease { + minifyEnabled true + shrinkResources true + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + signingConfig signingConfigs.winrelease + } + /*debug{ + minifyEnabled true + shrinkResources true + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + }*/ + } + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + kotlinOptions { + jvmTarget = '1.8' + } +} + +dependencies { + + implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" + implementation 'androidx.core:core-ktx:1.3.2' + implementation 'androidx.appcompat:appcompat:1.2.0' + implementation 'com.google.android.material:material:1.3.0' + implementation 'androidx.constraintlayout:constraintlayout:2.0.4' + implementation 'androidx.navigation:navigation-fragment-ktx:2.3.3' + implementation 'androidx.navigation:navigation-ui-ktx:2.3.3' + testImplementation 'junit:junit:4.13.2' + androidTestImplementation 'androidx.test.ext:junit:1.1.2' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0' + implementation 'com.lapism:search:2.4.1@aar' +} + +andResGuard { + // mappingFile = file("./resource_mapping.txt") + mappingFile = null + use7zip = true + useSign = true + // It will keep the origin path of your resources when it's true + keepRoot = false + // If set, name column in arsc those need to proguard will be kept to this value + fixedResName = "arg" + // It will merge the duplicated resources, but don't rely on this feature too much. + // it's always better to remove duplicated resource from repo + mergeDuplicatedRes = true + whiteList = [ + "R.drawable.load", + // your icon + "R.drawable.icon", + // for fabric + "R.string.com.crashlytics.*", + // for google-services + "R.string.google_app_id", + "R.string.gcm_defaultSenderId", + "R.string.default_web_client_id", + "R.string.ga_trackingId", + "R.string.firebase_database_url", + "R.string.google_api_key", + "R.string.google_crash_reporting_api_key" + ] + compressFilePattern = [ + "*.png", + "*.jpg", + "*.jpeg", + "*.gif", + "resources.arsc" + ] + sevenzip { + artifact = 'com.tencent.mm:SevenZip:1.2.20' + //path = "/usr/local/bin/7za" + } +} \ No newline at end of file diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro new file mode 100644 index 0000000..2820d08 --- /dev/null +++ b/app/proguard-rules.pro @@ -0,0 +1,31 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile + +-assumenosideeffects class android.util.Log { + public static *** d(...); + public static *** e(...); + public static *** i(...); + public static *** v(...); + public static *** println(...); + public static *** w(...); + public static *** wtf(...); +} diff --git a/app/src/androidTest/java/top/fumiama/simpledict/ExampleInstrumentedTest.kt b/app/src/androidTest/java/top/fumiama/simpledict/ExampleInstrumentedTest.kt new file mode 100644 index 0000000..c4365a1 --- /dev/null +++ b/app/src/androidTest/java/top/fumiama/simpledict/ExampleInstrumentedTest.kt @@ -0,0 +1,24 @@ +package top.fumiama.simpledict + +import androidx.test.platform.app.InstrumentationRegistry +import androidx.test.ext.junit.runners.AndroidJUnit4 + +import org.junit.Test +import org.junit.runner.RunWith + +import org.junit.Assert.* + +/** + * Instrumented test, which will execute on an Android device. + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +@RunWith(AndroidJUnit4::class) +class ExampleInstrumentedTest { + @Test + fun useAppContext() { + // Context of the app under test. + val appContext = InstrumentationRegistry.getInstrumentation().targetContext + assertEquals("top.fumiama.simpledict", appContext.packageName) + } +} \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..3f7dc3d --- /dev/null +++ b/app/src/main/AndroidManifest.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/java/top/fumiama/simpledict/Client.kt b/app/src/main/java/top/fumiama/simpledict/Client.kt new file mode 100644 index 0000000..d3768ae --- /dev/null +++ b/app/src/main/java/top/fumiama/simpledict/Client.kt @@ -0,0 +1,94 @@ +package top.fumiama.simpledict + +import android.util.Log +import java.io.IOException +import java.io.InputStream +import java.io.OutputStream +import java.net.Socket +import java.nio.charset.Charset + +class Client(val ip: String, val port: Int) { + //普通数据交互接口 + private var sc: Socket? = null + + //普通交互流 + private var dout: OutputStream? = null + private var din: InputStream? = null + + //已连接标记 + var isConnect = false + + /** + * 初始化普通交互连接 + */ + fun initConnect(){ + 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() + } + } catch (e: IOException) { //获取输入输出流是可能报IOException的,所以必须try-catch + e.printStackTrace() + } + } + + /** + * 发送数据至服务器 + * @param message 要发送至服务器的字符串 + */ + 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") + Log.d("MyC", "send message succeed") + } else Log.d("MyC", "no connect to send message") + } catch (e: IOException) { + Log.d("MyC", "send message to cilent failed") + e.printStackTrace() + } + } + + fun receiveMessage(): String? { + var message: String? = "" + try { + if (isConnect) { + Log.d("MyC", "开始接收服务端信息") + val inMessage = ByteArray(1024) //设置接受缓冲,避免接受数据过长占用过多内存 + val a = din?.read(inMessage) //a存储返回消息的长度 + if (a == null || a <= -1) return null + Log.d("MyC", "reply length:$a") + message = inMessage.copyOf(a).decodeToString() + Log.d("MyC", message) + } else Log.d("MyC", "no connect to receive message") + } catch (e: IOException) { + Log.d("MyC", "receive message failed") + e.printStackTrace() + } + return message + } + + /** + * 关闭连接 + */ + fun closeConnect() { + try { + din?.close() + dout?.close() + sc?.close() + } 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 new file mode 100644 index 0000000..ca26f73 --- /dev/null +++ b/app/src/main/java/top/fumiama/simpledict/MainActivity.kt @@ -0,0 +1,176 @@ +package top.fumiama.simpledict + +import android.annotation.SuppressLint +import android.content.SharedPreferences +import android.os.Bundle +import android.util.Log +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.EditText +import android.widget.TextView +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.dialog.MaterialAlertDialogBuilder +import com.google.android.material.snackbar.Snackbar +import com.lapism.search.internal.SearchLayout +import com.lapism.search.util.SearchUtils +import kotlinx.android.synthetic.main.activity_main.* +import kotlinx.android.synthetic.main.line_word.view.* +import java.lang.Thread.sleep + +class MainActivity : AppCompatActivity() { + private var keys = arrayOf() + private var datas = arrayOf() + private val dict = SimpleDict(Client("192.168.98.2", 8000)) + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_main) + + ffms.apply { + setAdapterLayoutManager(LinearLayoutManager(this@MainActivity)) + val adapter = ViewData(findViewById(R.id.search_recycler_view)).RecyclerViewAdapter() + setAdapter(adapter) + navigationIconSupport = SearchLayout.NavigationIconSupport.SEARCH + setOnNavigationClickListener(object : SearchLayout.OnNavigationClickListener { + override fun onNavigationClick(hasFocus: Boolean) { + if (hasFocus()) clearFocus() + else requestFocus() + } + }) + setTextHint(android.R.string.search_go) + setOnQueryTextListener(object : SearchLayout.OnQueryTextListener { + val sysTime get() = System.currentTimeMillis() / 1000 + var lastVisitTime = sysTime + val isLast get() = sysTime - lastVisitTime > 1 + var hasLoad = true + var key: CharSequence = "" + set(value) { + field = value + lastVisitTime = sysTime + hasLoad = false + } + + init { + Thread { + while (true) { + sleep(1) + if (isLast && !hasLoad) { + adapter.filter(key) + hasLoad = true + } + } + }.start() + } + + override fun onQueryTextChange(newText: CharSequence): Boolean { + if (newText.isNotEmpty()) key = newText + return true + } + + override fun onQueryTextSubmit(query: CharSequence): Boolean { + if(query.isNotEmpty()) Thread{ + val data = dict[query] + runOnUiThread { + showDictAlert(query.toString(), data) + } + }.start() + return true + } + }) + setOnMicClickListener(object : SearchLayout.OnMicClickListener { + override fun onMicClick() { + if (SearchUtils.isVoiceSearchAvailable(this@MainActivity)) { + SearchUtils.setVoiceSearch(this@MainActivity, "speak") + } + } + }) + setOnFocusChangeListener(object : SearchLayout.OnFocusChangeListener { + override fun onFocusChange(hasFocus: Boolean) { + navigationIconSupport = if (hasFocus) SearchLayout.NavigationIconSupport.ARROW + else SearchLayout.NavigationIconSupport.SEARCH + } + }) + } + } + + override fun onBackPressed() { + if(ffms.hasFocus()) ffms.clearFocus() + else super.onBackPressed() + } + + private fun showDictAlert(key: String, data: String?) { + AlertDialog.Builder(this@MainActivity) + .setTitle(key) + .setMessage(data) + .setPositiveButton(if(data != "null") "重设" else "添加") { _, _ -> + val t = EditText(this@MainActivity) + AlertDialog.Builder(this@MainActivity) + .setTitle("重设$key") + .setView(t) + .setPositiveButton(android.R.string.ok) { _, _ -> + if (t.text.isNotEmpty()) Thread { + dict[key] = t.text.toString() + }.start() + } + .setNegativeButton(android.R.string.cancel) { _, _ -> } + .show() + } + .setNeutralButton("收藏") { _, _ -> + getSharedPreferences("dict", MODE_PRIVATE)?.edit()?.let { + it.putString(key, data) + it.apply() + } + } + .setNegativeButton(android.R.string.cancel) { _, _ -> } + .show() + } + + inner class ViewData(itemView: View) : RecyclerView.ViewHolder(itemView) { + inner class RecyclerViewAdapter : + RecyclerView.Adapter() { + var count = 0 + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewData { + return ViewData( + LayoutInflater.from(parent.context) + .inflate(R.layout.line_word, parent, false) + ) + } + + @SuppressLint("ClickableViewAccessibility", "SetTextI18n") + override fun onBindViewHolder(holder: ViewData, position: Int) { + Log.d("MyMain", "Bind $position") + if(position < keys.size) { + holder.itemView.ta.text = keys[position] + if(position < datas.size) holder.itemView.tb.text = datas[position] + holder.itemView.setOnClickListener { + showDictAlert(keys[position], if(position < datas.size) datas[position] else "null") + } + } + } + + override fun getItemCount() = count + + fun filter(text: CharSequence) { + dict.pattern = text + dict.keys.let { + count = it.size + if (count > 0) { + keys = arrayOf() + datas = arrayOf() + it.forEach { + keys += it + datas += dict[it] + Log.d("MyMain", "Get key: $it is ${datas.last()}") + } + } + } + runOnUiThread { notifyDataSetChanged() } + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/top/fumiama/simpledict/SimpleDict.kt b/app/src/main/java/top/fumiama/simpledict/SimpleDict.kt new file mode 100644 index 0000000..741dd4f --- /dev/null +++ b/app/src/main/java/top/fumiama/simpledict/SimpleDict.kt @@ -0,0 +1,55 @@ +package top.fumiama.simpledict + +import java.lang.Thread.sleep + +class SimpleDict(private val client: Client): HashMap() { //must run in thread + var pattern: CharSequence = "a" + private var isInit = true + override val keys: MutableSet + get() { + val re = mutableSetOf() + if(isInit) { + isInit = false + return re + } else { + client.initConnect() + client.sendMessage("lst") + sleep(233) + client.receiveMessage() + client.sendMessage(pattern) + client.receiveMessage()?.substringBeforeLast('\n')?.split('\n')?.forEach { + re.add(it) + } + client.sendMessage("quit") + client.closeConnect() + return re + } + } + + override fun get(key: String): String? { + client.initConnect() + client.sendMessage("get") + sleep(233) + client.receiveMessage() + client.sendMessage(key) + val re = client.receiveMessage() + client.sendMessage("quit") + client.closeConnect() + return re + } + + override fun put(key: String, value: String): String? { + val p = this[key] + client.initConnect() + client.sendMessage("set") + sleep(233) + client.receiveMessage() + client.sendMessage(key) + client.receiveMessage() + client.sendMessage(value) + client.receiveMessage() + client.sendMessage("quit") + client.closeConnect() + return p + } +} \ No newline at end of file diff --git a/app/src/main/res/drawable-anydpi/ic_launcher_background.xml b/app/src/main/res/drawable-anydpi/ic_launcher_background.xml new file mode 100644 index 0000000..9486190 --- /dev/null +++ b/app/src/main/res/drawable-anydpi/ic_launcher_background.xml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml new file mode 100644 index 0000000..9d773cd --- /dev/null +++ b/app/src/main/res/layout/activity_main.xml @@ -0,0 +1,13 @@ + + + + + + \ 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 new file mode 100644 index 0000000..8025e1c --- /dev/null +++ b/app/src/main/res/layout/line_word.xml @@ -0,0 +1,31 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml new file mode 100644 index 0000000..54d4f81 --- /dev/null +++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher.png b/app/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000..a571e60 Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher.png b/app/src/main/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000..c41dd28 Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/app/src/main/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000..6dba46d Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000..15ac681 Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000..f25a419 Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/app/src/main/res/values-night/themes.xml b/app/src/main/res/values-night/themes.xml new file mode 100644 index 0000000..cdc2c8f --- /dev/null +++ b/app/src/main/res/values-night/themes.xml @@ -0,0 +1,16 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml new file mode 100644 index 0000000..699a436 --- /dev/null +++ b/app/src/main/res/values/colors.xml @@ -0,0 +1,14 @@ + + + #FFBB86FC + #FF6200EE + #FF3700B3 + #FF03DAC5 + #FF018786 + #FF000000 + #FFFFFFFF + #BA8D08 + #FF9800 + #FF845E + #FF5722 + \ No newline at end of file diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml new file mode 100644 index 0000000..125df87 --- /dev/null +++ b/app/src/main/res/values/dimens.xml @@ -0,0 +1,3 @@ + + 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 new file mode 100644 index 0000000..87f9780 --- /dev/null +++ b/app/src/main/res/values/strings.xml @@ -0,0 +1,12 @@ + + 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/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml new file mode 100644 index 0000000..dd109af --- /dev/null +++ b/app/src/main/res/values/themes.xml @@ -0,0 +1,27 @@ + + + + + + +