Skip to content

Commit

Permalink
refactor: transform FlexibleAdapter with BaseDifferAdapter
Browse files Browse the repository at this point in the history
  • Loading branch information
WhiredPlanck committed Nov 3, 2024
1 parent c70f56c commit d6fffba
Show file tree
Hide file tree
Showing 7 changed files with 86 additions and 130 deletions.
3 changes: 3 additions & 0 deletions app/src/main/java/com/osfans/trime/data/db/ClipboardHelper.kt
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,8 @@ object ClipboardHelper :

suspend fun get(id: Int) = clbDao.get(id)

suspend fun haveUnpinned() = clbDao.haveUnpinned()

suspend fun getAll() = clbDao.getAll()

suspend fun pin(id: Int) = clbDao.updatePinned(id, true)
Expand All @@ -107,6 +109,7 @@ object ClipboardHelper :
} else {
clbDao.deleteAll()
}
updateItemCount()
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ object CollectionHelper : CoroutineScope by CoroutineScope(SupervisorJob() + Dis

suspend fun insert(bean: DatabaseBean) = cltDao.insert(bean)

suspend fun haveUnpinned() = cltDao.haveUnpinned()

suspend fun getAll() = cltDao.getAll()

suspend fun pin(id: Int) = cltDao.updatePinned(id, true)
Expand Down
5 changes: 4 additions & 1 deletion app/src/main/java/com/osfans/trime/data/db/DatabaseDao.kt
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ interface DatabaseDao {
@Query("DELETE FROM ${DatabaseBean.TABLE_NAME} WHERE NOT pinned")
suspend fun deleteAllUnpinned()

@Query("SELECT * FROM ${DatabaseBean.TABLE_NAME}")
@Query("SELECT * FROM ${DatabaseBean.TABLE_NAME} ORDER BY pinned DESC, time DESC")
suspend fun getAll(): List<DatabaseBean>

@Query("SELECT * FROM ${DatabaseBean.TABLE_NAME} WHERE id=:id LIMIT 1")
Expand All @@ -57,6 +57,9 @@ interface DatabaseDao {
@Query("SELECT * FROM ${DatabaseBean.TABLE_NAME} WHERE rowId=:rowId LIMIT 1")
suspend fun get(rowId: Long): DatabaseBean?

@Query("SELECT EXISTS(SELECT 1 FROM ${DatabaseBean.TABLE_NAME} WHERE pinned=0)")
suspend fun haveUnpinned(): Boolean

@Query("SELECT COUNT(*) FROM ${DatabaseBean.TABLE_NAME}")
suspend fun itemCount(): Int
}
3 changes: 3 additions & 0 deletions app/src/main/java/com/osfans/trime/data/db/DraftHelper.kt
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ object DraftHelper : CoroutineScope by CoroutineScope(SupervisorJob() + Dispatch

suspend fun get(id: Int) = dftDao.get(id)

suspend fun haveUnpinned() = dftDao.haveUnpinned()

suspend fun getAll() = dftDao.getAll()

suspend fun pin(id: Int) = dftDao.updatePinned(id, true)
Expand All @@ -75,6 +77,7 @@ object DraftHelper : CoroutineScope by CoroutineScope(SupervisorJob() + Dispatch
} else {
dftDao.deleteAll()
}
updateItemCount()
}

fun onInputEventChanged() {
Expand Down
69 changes: 26 additions & 43 deletions app/src/main/java/com/osfans/trime/ime/symbol/DbAdapter.kt
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import com.osfans.trime.util.ShortcutUtils
import kotlinx.coroutines.launch

class DbAdapter(
private val context: Context,
private val ctx: Context,
private val service: TrimeInputMethodService,
theme: Theme,
) : FlexibleAdapter(theme) {
Expand All @@ -34,8 +34,9 @@ class DbAdapter(
SymbolBoardType.CLIPBOARD -> ClipboardHelper.pin(bean.id)
SymbolBoardType.COLLECTION -> CollectionHelper.pin(bean.id)
SymbolBoardType.DRAFT -> DraftHelper.pin(bean.id)
else -> return@launch
else -> {}
}
refresh()
}
}

Expand All @@ -45,8 +46,9 @@ class DbAdapter(
SymbolBoardType.CLIPBOARD -> ClipboardHelper.unpin(bean.id)
SymbolBoardType.COLLECTION -> CollectionHelper.unpin(bean.id)
SymbolBoardType.DRAFT -> DraftHelper.unpin(bean.id)
else -> return@launch
else -> {}
}
refresh()
}
}

Expand All @@ -56,13 +58,14 @@ class DbAdapter(
SymbolBoardType.CLIPBOARD -> ClipboardHelper.delete(bean.id)
SymbolBoardType.COLLECTION -> CollectionHelper.delete(bean.id)
SymbolBoardType.DRAFT -> DraftHelper.delete(bean.id)
else -> return@launch
else -> {}
}
refresh()
}
}

override fun onEdit(bean: DatabaseBean) {
bean.text?.let { ShortcutUtils.launchLiquidKeyboardEdit(context, type, bean.id, it) }
bean.text?.let { ShortcutUtils.launchLiquidKeyboardEdit(ctx, type, bean.id, it) }
}

override fun onCollect(bean: DatabaseBean) {
Expand All @@ -71,54 +74,34 @@ class DbAdapter(

// FIXME: 这个方法可能实现得比较粗糙,需要日后改进
override fun onDeleteAll() {
fun deleteAll() {
if (beans.all { it.pinned }) {
// 如果没有未置顶的条目,则删除所有已置顶的条目
service.lifecycleScope.launch {
when (type) {
SymbolBoardType.CLIPBOARD -> ClipboardHelper.deleteAll(false)
SymbolBoardType.COLLECTION -> CollectionHelper.deleteAll(false)
SymbolBoardType.DRAFT -> DraftHelper.deleteAll(false)
else -> return@launch
}
}
updateBeans(emptyList())
} else {
// 如果有已置顶的条目,则删除所有未置顶的条目
service.lifecycleScope.launch {
when (type) {
SymbolBoardType.CLIPBOARD -> {
ClipboardHelper.deleteAll()
updateBeans(ClipboardHelper.getAll())
}

SymbolBoardType.COLLECTION -> {
CollectionHelper.deleteAll()
updateBeans(CollectionHelper.getAll())
}

SymbolBoardType.DRAFT -> {
DraftHelper.deleteAll()
updateBeans(DraftHelper.getAll())
}

else -> return@launch
}
}
}
}

val confirm =
AlertDialog
.Builder(context)
.setTitle(R.string.delete_all)
.setMessage(R.string.liquid_keyboard_ask_to_delete_all)
.setPositiveButton(R.string.ok) { _, _ ->
deleteAll()
service.lifecycleScope.launch {
when (type) {
SymbolBoardType.CLIPBOARD -> ClipboardHelper.deleteAll(ClipboardHelper.haveUnpinned())
SymbolBoardType.COLLECTION -> CollectionHelper.deleteAll(CollectionHelper.haveUnpinned())
SymbolBoardType.DRAFT -> DraftHelper.deleteAll(DraftHelper.haveUnpinned())
else -> {}
}
refresh()
}
}.setNegativeButton(R.string.cancel, null)
.create()
service.inputView?.showDialog(confirm)
}

override val showCollectButton: Boolean = type != SymbolBoardType.COLLECTION

private suspend fun refresh() {
when (type) {
SymbolBoardType.CLIPBOARD -> submitList(ClipboardHelper.getAll())
SymbolBoardType.COLLECTION -> submitList(CollectionHelper.getAll())
SymbolBoardType.DRAFT -> submitList(DraftHelper.getAll())
else -> {}
}
}
}
130 changes: 46 additions & 84 deletions app/src/main/java/com/osfans/trime/ime/symbol/FlexibleAdapter.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,15 @@

package com.osfans.trime.ime.symbol

import android.content.Context
import android.os.Build
import android.view.ViewGroup
import android.widget.PopupMenu
import androidx.annotation.DrawableRes
import androidx.annotation.StringRes
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.RecyclerView
import com.chad.library.adapter4.BaseDifferAdapter
import com.osfans.trime.R
import com.osfans.trime.data.db.DatabaseBean
import com.osfans.trime.data.theme.Theme
Expand All @@ -19,79 +22,63 @@ import kotlin.math.min

abstract class FlexibleAdapter(
private val theme: Theme,
) : RecyclerView.Adapter<FlexibleAdapter.ViewHolder>() {
private val mBeans = mutableListOf<DatabaseBean>()

// 映射条目的 id 和其在视图中位置的关系
// 以应对增删条目时 id 和其位置的相对变化
// [<id, position>, ...]
private val mBeansId = mutableMapOf<Int, Int>()
val beans get() = mBeans

fun updateBeans(beans: List<DatabaseBean>) {
val sorted =
beans.sortedWith { b1, b2 ->
when {
// 如果 b1 置顶而 b2 没置顶,则 b1 比 b2 小,排前面
b1.pinned && !b2.pinned -> -1
// 如果 b1 没置顶而 b2 置顶,则 b1 比 b2 大,排后面
!b1.pinned && b2.pinned -> 1
// 如果都置顶了或都没置顶,则比较 id,id 大的排前面
else -> b2.id.compareTo(b1.id)
}
) : BaseDifferAdapter<DatabaseBean, FlexibleAdapter.ViewHolder>(diffCallback) {
companion object {
private val diffCallback =
object : DiffUtil.ItemCallback<DatabaseBean>() {
override fun areItemsTheSame(
oldItem: DatabaseBean,
newItem: DatabaseBean,
): Boolean = oldItem.id == newItem.id

override fun areContentsTheSame(
oldItem: DatabaseBean,
newItem: DatabaseBean,
): Boolean = oldItem == newItem
}
val prevSize = mBeans.size
mBeans.clear()
notifyItemRangeRemoved(0, prevSize)
mBeans.addAll(sorted)
notifyItemRangeChanged(0, sorted.size)
mBeansId.clear()
mBeans.forEachIndexed { index: Int, (id): DatabaseBean ->
mBeansId[id] = index
}
}

private fun excerptText(
str: String,
lines: Int = 4,
chars: Int = 128,
): String =
buildString {
val length = str.length
var lineBreak = -1
for (i in 1..lines) {
val start = lineBreak + 1 // skip previous '\n'
val excerptEnd = min(start + chars, length)
lineBreak = str.indexOf('\n', start)
if (lineBreak < 0) {
// no line breaks remaining, substring to end of text
append(str.substring(start, excerptEnd))
break
} else {
val end = min(excerptEnd, lineBreak)
// append one line exactly
appendLine(str.substring(start, end))
private fun excerptText(
str: String,
lines: Int = 4,
chars: Int = 128,
): String =
buildString {
val length = str.length
var lineBreak = -1
for (i in 1..lines) {
val start = lineBreak + 1 // skip previous '\n'
val excerptEnd = min(start + chars, length)
lineBreak = str.indexOf('\n', start)
if (lineBreak < 0) {
// no line breaks remaining, substring to end of text
append(str.substring(start, excerptEnd))
break
} else {
val end = min(excerptEnd, lineBreak)
// append one line exactly
appendLine(str.substring(start, end))
}
}
}
}

override fun getItemCount(): Int = mBeans.size
}

override fun onCreateViewHolder(
context: Context,
parent: ViewGroup,
viewType: Int,
): ViewHolder = ViewHolder(SimpleItemUi(parent.context, theme))
): ViewHolder = ViewHolder(SimpleItemUi(context, theme))

class ViewHolder(
val ui: SimpleItemUi,
) : RecyclerView.ViewHolder(ui.root)

override fun onBindViewHolder(
viewHolder: ViewHolder,
holder: ViewHolder,
position: Int,
item: DatabaseBean?,
) {
with(viewHolder.ui) {
val bean = mBeans[position]
with(holder.ui) {
val bean = item ?: return
setItem(excerptText(bean.text ?: ""), bean.pinned)
root.setOnClickListener {
onPaste(bean)
Expand Down Expand Up @@ -119,12 +106,10 @@ abstract class FlexibleAdapter(
if (bean.pinned) {
menuItem(R.string.simple_key_unpin, R.drawable.ic_outline_push_pin_24) {
onUnpin(bean)
setPinStatus(bean.id, false)
}
} else {
menuItem(R.string.simple_key_pin, R.drawable.ic_baseline_push_pin_24) {
onPin(bean)
setPinStatus(bean.id, true)
}
}
if (showCollectButton) {
Expand All @@ -134,12 +119,9 @@ abstract class FlexibleAdapter(
}
menuItem(R.string.delete, R.drawable.ic_baseline_delete_24) {
onDelete(bean)
delete(bean.id)
}
if (beans.isNotEmpty()) {
menuItem(R.string.delete_all, R.drawable.ic_baseline_delete_sweep_24) {
onDeleteAll()
}
menuItem(R.string.delete_all, R.drawable.ic_baseline_delete_sweep_24) {
onDeleteAll()
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
menu.setForceShowIcon(true)
Expand All @@ -150,26 +132,6 @@ abstract class FlexibleAdapter(
}
}

private fun delete(id: Int) {
val position = mBeansId.getValue(id)
mBeans.removeAt(position)
mBeansId.remove(id)
for (i in position until mBeans.size) {
mBeansId[mBeans[i].id] = i
}
notifyItemRemoved(position)
}

private fun setPinStatus(
id: Int,
pinned: Boolean,
) {
val position = mBeansId.getValue(id)
mBeans[position] = mBeans[position].copy(pinned = pinned)
// 置顶会改变条目的排列顺序
updateBeans(mBeans)
}

abstract fun onPaste(bean: DatabaseBean)

abstract fun onPin(bean: DatabaseBean)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ class LiquidKeyboard(
}

service.lifecycleScope.launch {
dbAdapter.updateBeans(data())
dbAdapter.submitList(data())
}
}

Expand Down Expand Up @@ -246,7 +246,7 @@ class LiquidKeyboard(
if (currentBoardType == SymbolBoardType.CLIPBOARD) {
Timber.v("OnClipboardUpdateListener onUpdate: update clipboard view")
service.lifecycleScope.launch {
dbAdapter.updateBeans(ClipboardHelper.getAll())
dbAdapter.submitList(ClipboardHelper.getAll())
}
}
}
Expand Down

0 comments on commit d6fffba

Please sign in to comment.