package top.fumiama.simpledict import android.annotation.SuppressLint import android.content.ClipData import android.content.ClipboardManager import android.content.Context import android.os.Bundle import android.text.SpannableString import android.text.Spanned import android.text.style.StrikethroughSpan import android.util.Log import android.view.LayoutInflater import android.view.MotionEvent import android.view.View import android.view.ViewGroup import android.view.inputmethod.InputMethodManager import android.widget.ImageButton import android.widget.Toast import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AppCompatActivity import androidx.core.content.edit import androidx.core.view.children import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import com.lapism.search.internal.SearchLayout import kotlinx.android.synthetic.main.activity_main.* import kotlinx.android.synthetic.main.activity_main.ffsw import kotlinx.android.synthetic.main.activity_main.view.* import kotlinx.android.synthetic.main.dialog_input.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.tn import java.io.FileNotFoundException import java.lang.Exception class MainActivity : AppCompatActivity() { private var host = "127.0.0.1" private var port = 80 private var pwd = "demo" private var dict: SimpleDict? = null private var hasLiked = false private var cm: ClipboardManager? = null private var ad: ListViewHolder.RecyclerViewAdapter? = null private var lastLikeLine: View? = null @SuppressLint("ClickableViewAccessibility") override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) val ime = getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager getSharedPreferences("remote", MODE_PRIVATE)?.apply { if(contains("host")) getString("host", host)?.apply { host = this } if(contains("port")) getInt("port", port).apply { port = this } if(contains("pwd")) getString("pwd", pwd)?.apply { pwd = this } } dict = SimpleDict(Client(host, port), pwd) ad = LikeViewHolder(ffr).RecyclerViewAdapter() cm = getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager ffr.apply { layoutManager = LinearLayoutManager(this@MainActivity) adapter = ad setOnScrollChangeListener { _, _, scrollY, _, _ -> this@MainActivity.ffsw.isEnabled = scrollY == 0 } } ffsw.apply { setOnRefreshListener { fetchThread{ updateSize() } } isRefreshing = true fetchThread { updateSize() } } ffms.apply { val recyclerView = findViewById(R.id.search_recycler_view) setAdapterLayoutManager(LinearLayoutManager(this@MainActivity)) val adapter = SearchViewHolder(recyclerView).RecyclerViewAdapter() setAdapter(adapter) navigationIconSupport = SearchLayout.NavigationIconSupport.SEARCH setMicIconImageResource(R.drawable.ic_setting) val micView = findViewById(R.id.search_image_view_mic) setClearFocusOnBackPressed(true) setOnNavigationClickListener(object : SearchLayout.OnNavigationClickListener { override fun onNavigationClick(hasFocus: Boolean) { if (hasFocus()) { if(hasLiked) ad?.refresh() clearFocus() } else requestFocus() } }) setTextHint(android.R.string.search_go) setOnQueryTextListener(object : SearchLayout.OnQueryTextListener { var lastChangeTime = 0L override fun onQueryTextChange(newText: CharSequence): Boolean { postDelayed({ val diff = System.currentTimeMillis() - lastChangeTime if(diff > 500) { if (newText.isNotEmpty()) adapter.refresh(newText) } }, 1024) lastChangeTime = System.currentTimeMillis() return true } override fun onQueryTextSubmit(query: CharSequence): Boolean { if(query.isNotEmpty()) { val key = query.toString() val data = dict?.get(key) showDictAlert(key, data, recyclerView.children.toList().let { val i = it.map { it.ta.text }.indexOf(key) if(i >= 0) it[i] else null }) } return true } }) setOnMicClickListener(object : SearchLayout.OnMicClickListener { override fun onMicClick() { /*if (SearchUtils.isVoiceSearchAvailable(this@MainActivity)) { SearchUtils.setVoiceSearch(this@MainActivity, "please speak") }*/ val t = layoutInflater.inflate(R.layout.dialog_input, null, false) AlertDialog.Builder(this@MainActivity) .setView(t) .setTitle("提示") .setPositiveButton(android.R.string.ok) { _, _ -> val info = t.diet.text.toString() try { val h = info.substringBefore(':') val l = info.substringAfter(':') val p = l.substringBefore('_').toInt() val w = l.substringAfter('_') if (h != "" && p > 0 && p < 65536 && w != "") { getSharedPreferences("remote", MODE_PRIVATE)?.edit { putString("host", h) putInt("port", p) putString("pwd", w) apply() Toast.makeText(this@MainActivity, "下次生效", Toast.LENGTH_SHORT).show() return@setPositiveButton } throw FileNotFoundException("getSharedPreferences named \"remote\" error.") } else throw IllegalArgumentException() } catch (e: Exception) { e.printStackTrace() Toast.makeText(this@MainActivity, "格式非法", Toast.LENGTH_SHORT).show() } } .setNegativeButton(android.R.string.cancel) { _, _ -> } .show() } }) setOnClearClickListener(object : SearchLayout.OnClearClickListener { override fun onClearClick() { Toast.makeText(this@MainActivity, "clear", Toast.LENGTH_SHORT).show() } }) setOnFocusChangeListener(object : SearchLayout.OnFocusChangeListener { override fun onFocusChange(hasFocus: Boolean) { navigationIconSupport = if (hasFocus) SearchLayout.NavigationIconSupport.ARROW else { micView.postDelayed({ micView.visibility = View.VISIBLE }, 233) SearchLayout.NavigationIconSupport.SEARCH } } }) this@MainActivity.ffc.setOnTouchListener { _, e -> if (e.action == MotionEvent.ACTION_UP && mSearchEditText?.text?.isNotEmpty() == true) { ime.hideSoftInputFromWindow(window.decorView.windowToken, 0) } false } } } override fun onBackPressed() { if(ffms.hasFocus()) { if(hasLiked) ad?.refresh() } else super.onBackPressed() } /*override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { super.onActivityResult(requestCode, resultCode, data) when(requestCode) { SearchUtils.SPEECH_REQUEST_CODE -> data?.getStringArrayListExtra(RecognizerIntent.EXTRA_RESULTS)?.let { if(it.isNotEmpty()) { ffms.requestFocus() ffms.mSearchEditText?.setText(it[0]) } } } }*/ private fun updateSize() { lastLikeLine?.fftc?.text = dict?.size?.toString()?:"0" } private fun fetchThread(doWhenFinish: (()->Unit)? = null) { Thread{ dict?.fetchDict { runOnUiThread { Toast.makeText(this@MainActivity, "刷新成功", Toast.LENGTH_SHORT).show() ffsw.isRefreshing = false ad?.refresh() doWhenFinish?.apply { this() } } } }.start() } private fun showDictAlert(key: String, data: String?, line: View?) { val hintAdd = if(data != null && data != "null") "重设" else "添加" hasLiked = false AlertDialog.Builder(this@MainActivity) .setTitle(key) .setMessage(data) .setPositiveButton(hintAdd) { _, _ -> val t = layoutInflater.inflate(R.layout.dialog_input, null, false) t.diet.setText(data) t.dit.text = "更改将立即生效" AlertDialog.Builder(this@MainActivity) .setTitle("$hintAdd$key") .setView(t) .setPositiveButton(android.R.string.ok) { _, _ -> val newText = t.diet.text.toString() if (t.diet.text.isNotEmpty() && newText != data) Thread { dict?.set(key, newText) line?.tb?.text = newText updateSize() }.start() else Toast.makeText(this, "未更改", Toast.LENGTH_SHORT).show() } .setNegativeButton(android.R.string.cancel) { _, _ -> } .show() } .setNeutralButton("删除") { _, _ -> Thread{ dict?.minusAssign(key) line?.apply { val delKey = SpannableString(key) val delData = SpannableString(data) delKey.setSpan(StrikethroughSpan(), 0, key.length, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE) delData.setSpan(StrikethroughSpan(), 0, (data?.length?:0), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE) ta.text = delKey tn.text = delKey tb.text = delData updateSize() } }.start() } .setNegativeButton(android.R.string.cancel) { _, _ -> } .show() } inner class SearchViewHolder(itemView: View) : ListViewHolder(itemView) { inner class RecyclerViewAdapter : ListViewHolder.RecyclerViewAdapter() { override fun getKeys(filterText: CharSequence?) = filterText?.let { filter(it) } override fun getValue(key: String) = dict?.get(key) private fun filter(text: CharSequence): List { val selectSet = dict?.keys?.filter { it.contains(text, true) }?.toSet()?.plus(dict?.filterValues { it?.contains(text, true) ?: false }.let { val newSet = mutableSetOf() it?.keys?.forEach { newSet += it } newSet }) return selectSet?.toList()?.let { if (it.size > 50) it.subList(0, 49) else it }?: emptyList() } } } inner class LikeViewHolder(itemView: View) : ListViewHolder(itemView) { inner class RecyclerViewAdapter: ListViewHolder.RecyclerViewAdapter(true){ var capacity = 5 override fun loadMore() { capacity += 5 refresh() } override fun getKeys(filterText: CharSequence?) = getSharedPreferences("dict", MODE_PRIVATE).all.keys.toTypedArray().let{ dict?.let { d -> val end = d.latestKeys.size val start = if(end > capacity) end - capacity else 0 (it + d.latestKeys.copyOfRange(start, end).reversedArray()).toList() }?: emptyList() } override fun getValue(key: String) = dict?.get(key)?:getSharedPreferences("dict", MODE_PRIVATE).getString(key, "null") } } open inner class ListViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { open inner class RecyclerViewAdapter(private val showLoadMore: Boolean = false) : RecyclerView.Adapter() { private var listKeys: List? = null open fun getKeys(filterText: CharSequence? = null): List? = null open fun getValue(key: String): String? = null open fun loadMore() {} override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ListViewHolder { return ListViewHolder( LayoutInflater.from(parent.context) .inflate(R.layout.line_word, parent, false) ) } @SuppressLint("ClickableViewAccessibility", "SetTextI18n") override fun onBindViewHolder(holder: ListViewHolder, position: Int) { Log.d("MyMain", "Bind open at $position") Thread{ listKeys?.apply { if (position < size) { val key = get(position) val data = getValue(key) val like = getSharedPreferences("dict", MODE_PRIVATE)?.contains(key) == true Log.d("MyMain", "Like status of $key is $like") holder.itemView.apply { runOnUiThread { ta.visibility = View.VISIBLE lwclast.visibility = View.GONE tn.text = key ta.text = key tb.text = data vl.setBackgroundResource(if(like) R.drawable.ic_like_filled else R.drawable.ic_like) Log.d("MyMain", "Set like of $key: $like") setOnClickListener { showDictAlert(key, data, this) } setOnLongClickListener { cm?.setPrimaryClip(ClipData.newPlainText("SimpleDict", "$key\n$data")) runOnUiThread { Toast.makeText(this@MainActivity, "已复制", Toast.LENGTH_SHORT).show() } true } 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() } } } } } else if(showLoadMore && position == size) runOnUiThread{ holder.itemView.apply { lastLikeLine = this ta.visibility = View.GONE lwclast.visibility = View.VISIBLE tn.text = "motkyep..." tb.text = "加载更多..." updateSize() setOnClickListener { loadMore() } } } } }.start() } override fun getItemCount() = (listKeys?.size?:0) + (if(showLoadMore) 1 else 0) fun refresh(filterText: CharSequence? = null) = Thread{ listKeys = getKeys(filterText) runOnUiThread { notifyDataSetChanged() } }.start() } } }