diff --git a/app/src/main/java/com/idormy/sms/forwarder/adapter/FrpcRecyclerAdapter.kt b/app/src/main/java/com/idormy/sms/forwarder/adapter/FrpcRecyclerAdapter.kt new file mode 100644 index 00000000..1e02cbbc --- /dev/null +++ b/app/src/main/java/com/idormy/sms/forwarder/adapter/FrpcRecyclerAdapter.kt @@ -0,0 +1,104 @@ +package com.idormy.sms.forwarder.adapter + +import android.annotation.SuppressLint +import android.view.LayoutInflater +import android.view.MotionEvent +import android.view.View +import android.view.ViewGroup +import android.widget.ImageView +import android.widget.TextView +import androidx.recyclerview.widget.ItemTouchHelper +import androidx.recyclerview.widget.RecyclerView +import com.idormy.sms.forwarder.R +import com.idormy.sms.forwarder.adapter.base.ItemMoveCallback +import com.idormy.sms.forwarder.database.entity.Frpc +import java.util.Collections + +@Suppress("DEPRECATION") +class FrpcRecyclerAdapter( + var itemList: MutableList, + private var removeClickListener: ((Int) -> Unit)? = null, + private var editClickListener: ((Int) -> Unit)? = null, +) : RecyclerView.Adapter(), ItemMoveCallback.Listener { + + private lateinit var touchHelper: ItemTouchHelper + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { + val view = LayoutInflater.from(parent.context).inflate(R.layout.adapter_frpc_list_item, parent, false) + return ViewHolder(view) + } + + override fun onBindViewHolder(holder: ViewHolder, position: Int) { + val item = itemList[position] + holder.bind(item) + } + + override fun getItemCount(): Int = itemList.size + + fun setTouchHelper(touchHelper: ItemTouchHelper) { + this@FrpcRecyclerAdapter.touchHelper = touchHelper + } + + @SuppressLint("ClickableViewAccessibility") + inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView), View.OnClickListener { + private val image: ImageView = itemView.findViewById(R.id.iv_image) + private val status: ImageView = itemView.findViewById(R.id.iv_status) + private val title: TextView = itemView.findViewById(R.id.tv_title) + private val editIcon: ImageView = itemView.findViewById(R.id.iv_edit) + private val removeIcon: ImageView = itemView.findViewById(R.id.iv_remove) + private val dragIcon: ImageView = itemView.findViewById(R.id.iv_drag) + + init { + if (removeClickListener == null) { + removeIcon.visibility = View.GONE + } else { + removeIcon.setOnClickListener(this) + } + + if (editClickListener == null) { + editIcon.visibility = View.GONE + } else { + editIcon.setOnClickListener(this) + } + + dragIcon.setOnTouchListener { _, event -> + if (event.actionMasked == MotionEvent.ACTION_DOWN) { + touchHelper.startDrag(this) + } + return@setOnTouchListener false + } + } + + fun bind(frpc: Frpc) { + image.setImageResource(frpc.imageId) + status.setImageResource(frpc.autorunImageId) + title.text = frpc.name + } + + override fun onClick(v: View?) { + val position = adapterPosition + if (position != RecyclerView.NO_POSITION) { + when (v?.id) { + R.id.iv_edit -> editClickListener?.let { it(position) } + R.id.iv_remove -> removeClickListener?.let { it(position) } + } + } + } + } + + override fun onItemMove(fromPosition: Int, toPosition: Int) { + if (fromPosition < toPosition) { + for (i in fromPosition until toPosition) { + Collections.swap(itemList, i, i + 1) + } + } else { + for (i in fromPosition downTo toPosition + 1) { + Collections.swap(itemList, i, i - 1) + } + } + notifyItemMoved(fromPosition, toPosition) + } + + override fun onDragFinished() {} +} + diff --git a/app/src/main/java/com/idormy/sms/forwarder/adapter/RuleRecyclerAdapter.kt b/app/src/main/java/com/idormy/sms/forwarder/adapter/RuleRecyclerAdapter.kt new file mode 100644 index 00000000..c9ad52c6 --- /dev/null +++ b/app/src/main/java/com/idormy/sms/forwarder/adapter/RuleRecyclerAdapter.kt @@ -0,0 +1,110 @@ +package com.idormy.sms.forwarder.adapter + +import android.annotation.SuppressLint +import android.view.LayoutInflater +import android.view.MotionEvent +import android.view.View +import android.view.ViewGroup +import android.widget.ImageView +import android.widget.TextView +import androidx.recyclerview.widget.ItemTouchHelper +import androidx.recyclerview.widget.RecyclerView +import com.idormy.sms.forwarder.R +import com.idormy.sms.forwarder.adapter.base.ItemMoveCallback +import com.idormy.sms.forwarder.database.entity.Rule +import java.util.Collections + +@Suppress("DEPRECATION") +class RuleRecyclerAdapter( + var itemList: MutableList, + private var removeClickListener: ((Int) -> Unit)? = null, + private var editClickListener: ((Int) -> Unit)? = null, +) : RecyclerView.Adapter(), ItemMoveCallback.Listener { + + private lateinit var touchHelper: ItemTouchHelper + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { + val view = LayoutInflater.from(parent.context).inflate(R.layout.adapter_rule_list_item, parent, false) + return ViewHolder(view) + } + + override fun onBindViewHolder(holder: ViewHolder, position: Int) { + val item = itemList[position] + holder.bind(item) + } + + override fun getItemCount(): Int = itemList.size + + fun setTouchHelper(touchHelper: ItemTouchHelper) { + this@RuleRecyclerAdapter.touchHelper = touchHelper + } + + @SuppressLint("ClickableViewAccessibility") + inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView), View.OnClickListener { + private val image: ImageView = itemView.findViewById(R.id.iv_image) + private val status: ImageView = itemView.findViewById(R.id.iv_status) + private val title: TextView = itemView.findViewById(R.id.tv_title) + private val editIcon: ImageView = itemView.findViewById(R.id.iv_edit) + private val removeIcon: ImageView = itemView.findViewById(R.id.iv_remove) + private val dragIcon: ImageView = itemView.findViewById(R.id.iv_drag) + + init { + if (removeClickListener == null) { + removeIcon.visibility = View.GONE + } else { + removeIcon.setOnClickListener(this) + } + + if (editClickListener == null) { + editIcon.visibility = View.GONE + } else { + editIcon.setOnClickListener(this) + } + + dragIcon.setOnTouchListener { _, event -> + if (event.actionMasked == MotionEvent.ACTION_DOWN) { + touchHelper.startDrag(this) + } + return@setOnTouchListener false + } + } + + fun bind(rule: Rule) { + val icon = when (rule.type) { + "sms" -> R.drawable.auto_task_icon_sms + "call" -> R.drawable.auto_task_icon_incall + "app" -> R.drawable.auto_task_icon_start_activity + else -> R.drawable.auto_task_icon_sms + } + image.setImageResource(icon) + status.setImageResource(rule.statusImageId) + title.text = rule.name + } + + override fun onClick(v: View?) { + val position = adapterPosition + if (position != RecyclerView.NO_POSITION) { + when (v?.id) { + R.id.iv_edit -> editClickListener?.let { it(position) } + R.id.iv_remove -> removeClickListener?.let { it(position) } + } + } + } + } + + override fun onItemMove(fromPosition: Int, toPosition: Int) { + if (fromPosition < toPosition) { + for (i in fromPosition until toPosition) { + Collections.swap(itemList, i, i + 1) + } + } else { + for (i in fromPosition downTo toPosition + 1) { + Collections.swap(itemList, i, i - 1) + } + } + notifyItemMoved(fromPosition, toPosition) + } + + override fun onDragFinished() {} +} + diff --git a/app/src/main/java/com/idormy/sms/forwarder/adapter/SenderRecyclerAdapter.kt b/app/src/main/java/com/idormy/sms/forwarder/adapter/SenderRecyclerAdapter.kt new file mode 100644 index 00000000..ac6f4c5d --- /dev/null +++ b/app/src/main/java/com/idormy/sms/forwarder/adapter/SenderRecyclerAdapter.kt @@ -0,0 +1,104 @@ +package com.idormy.sms.forwarder.adapter + +import android.annotation.SuppressLint +import android.view.LayoutInflater +import android.view.MotionEvent +import android.view.View +import android.view.ViewGroup +import android.widget.ImageView +import android.widget.TextView +import androidx.recyclerview.widget.ItemTouchHelper +import androidx.recyclerview.widget.RecyclerView +import com.idormy.sms.forwarder.R +import com.idormy.sms.forwarder.adapter.base.ItemMoveCallback +import com.idormy.sms.forwarder.database.entity.Sender +import java.util.Collections + +@Suppress("DEPRECATION") +class SenderRecyclerAdapter( + var itemList: MutableList, + private var removeClickListener: ((Int) -> Unit)? = null, + private var editClickListener: ((Int) -> Unit)? = null, +) : RecyclerView.Adapter(), ItemMoveCallback.Listener { + + private lateinit var touchHelper: ItemTouchHelper + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { + val view = LayoutInflater.from(parent.context).inflate(R.layout.adapter_sender_list_item, parent, false) + return ViewHolder(view) + } + + override fun onBindViewHolder(holder: ViewHolder, position: Int) { + val item = itemList[position] + holder.bind(item) + } + + override fun getItemCount(): Int = itemList.size + + fun setTouchHelper(touchHelper: ItemTouchHelper) { + this@SenderRecyclerAdapter.touchHelper = touchHelper + } + + @SuppressLint("ClickableViewAccessibility") + inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView), View.OnClickListener { + private val image: ImageView = itemView.findViewById(R.id.iv_image) + private val status: ImageView = itemView.findViewById(R.id.iv_status) + private val title: TextView = itemView.findViewById(R.id.tv_title) + private val editIcon: ImageView = itemView.findViewById(R.id.iv_edit) + private val removeIcon: ImageView = itemView.findViewById(R.id.iv_remove) + private val dragIcon: ImageView = itemView.findViewById(R.id.iv_drag) + + init { + if (removeClickListener == null) { + removeIcon.visibility = View.GONE + } else { + removeIcon.setOnClickListener(this) + } + + if (editClickListener == null) { + editIcon.visibility = View.GONE + } else { + editIcon.setOnClickListener(this) + } + + dragIcon.setOnTouchListener { _, event -> + if (event.actionMasked == MotionEvent.ACTION_DOWN) { + touchHelper.startDrag(this) + } + return@setOnTouchListener false + } + } + + fun bind(sender: Sender) { + image.setImageResource(sender.imageId) + status.setImageResource(sender.statusImageId) + title.text = sender.name + } + + override fun onClick(v: View?) { + val position = adapterPosition + if (position != RecyclerView.NO_POSITION) { + when (v?.id) { + R.id.iv_edit -> editClickListener?.let { it(position) } + R.id.iv_remove -> removeClickListener?.let { it(position) } + } + } + } + } + + override fun onItemMove(fromPosition: Int, toPosition: Int) { + if (fromPosition < toPosition) { + for (i in fromPosition until toPosition) { + Collections.swap(itemList, i, i + 1) + } + } else { + for (i in fromPosition downTo toPosition + 1) { + Collections.swap(itemList, i, i - 1) + } + } + notifyItemMoved(fromPosition, toPosition) + } + + override fun onDragFinished() {} +} + diff --git a/app/src/main/java/com/idormy/sms/forwarder/adapter/TaskSettingAdapter.kt b/app/src/main/java/com/idormy/sms/forwarder/adapter/TaskSettingAdapter.kt index 9a923701..69b6be7d 100644 --- a/app/src/main/java/com/idormy/sms/forwarder/adapter/TaskSettingAdapter.kt +++ b/app/src/main/java/com/idormy/sms/forwarder/adapter/TaskSettingAdapter.kt @@ -10,20 +10,21 @@ import android.widget.TextView import androidx.recyclerview.widget.ItemTouchHelper import androidx.recyclerview.widget.RecyclerView import com.idormy.sms.forwarder.R +import com.idormy.sms.forwarder.adapter.base.ItemMoveCallback import com.idormy.sms.forwarder.entity.TaskSetting +import java.util.Collections @Suppress("DEPRECATION") class TaskSettingAdapter( - val itemList: MutableList, - private val editClickListener: (Int) -> Unit, - private val removeClickListener: (Int) -> Unit + var itemList: MutableList, + private var removeClickListener: ((Int) -> Unit)? = null, + private var editClickListener: ((Int) -> Unit)? = null, ) : RecyclerView.Adapter(), ItemMoveCallback.Listener { private lateinit var touchHelper: ItemTouchHelper override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { - val view = LayoutInflater.from(parent.context) - .inflate(R.layout.adapter_task_setting_item, parent, false) + val view = LayoutInflater.from(parent.context).inflate(R.layout.adapter_task_setting_item, parent, false) return ViewHolder(view) } @@ -48,8 +49,17 @@ class TaskSettingAdapter( private val dragIcon: ImageView = itemView.findViewById(R.id.iv_drag) init { - editIcon.setOnClickListener(this) - removeIcon.setOnClickListener(this) + if (removeClickListener == null) { + removeIcon.visibility = View.GONE + } else { + removeIcon.setOnClickListener(this) + } + + if (editClickListener == null) { + editIcon.visibility = View.GONE + } else { + editIcon.setOnClickListener(this) + } dragIcon.setOnTouchListener { _, event -> if (event.actionMasked == MotionEvent.ACTION_DOWN) { @@ -69,8 +79,8 @@ class TaskSettingAdapter( val position = adapterPosition if (position != RecyclerView.NO_POSITION) { when (v?.id) { - R.id.iv_edit -> editClickListener(position) - R.id.iv_remove -> removeClickListener(position) + R.id.iv_edit -> editClickListener?.let { it(position) } + R.id.iv_remove -> removeClickListener?.let { it(position) } } } } @@ -79,11 +89,11 @@ class TaskSettingAdapter( override fun onItemMove(fromPosition: Int, toPosition: Int) { if (fromPosition < toPosition) { for (i in fromPosition until toPosition) { - itemList[i] = itemList.set(i + 1, itemList[i]) + Collections.swap(itemList, i, i + 1) } } else { for (i in fromPosition downTo toPosition + 1) { - itemList[i] = itemList.set(i - 1, itemList[i]) + Collections.swap(itemList, i, i - 1) } } notifyItemMoved(fromPosition, toPosition) @@ -91,40 +101,3 @@ class TaskSettingAdapter( override fun onDragFinished() {} } - -@Suppress("DEPRECATION") -class ItemMoveCallback(private val listener: Listener) : ItemTouchHelper.Callback() { - - interface Listener { - fun onItemMove(fromPosition: Int, toPosition: Int) - fun onDragFinished() - } - - override fun getMovementFlags( - recyclerView: RecyclerView, - viewHolder: RecyclerView.ViewHolder - ): Int { - val dragFlags = ItemTouchHelper.UP or ItemTouchHelper.DOWN - return makeMovementFlags(dragFlags, 0) - } - - override fun onMove( - recyclerView: RecyclerView, - viewHolder: RecyclerView.ViewHolder, - target: RecyclerView.ViewHolder - ): Boolean { - listener.onItemMove(viewHolder.adapterPosition, target.adapterPosition) - return true - } - - override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) { - // Swiping is not needed for this example - } - - override fun onSelectedChanged(viewHolder: RecyclerView.ViewHolder?, actionState: Int) { - super.onSelectedChanged(viewHolder, actionState) - if (actionState == ItemTouchHelper.ACTION_STATE_IDLE) { - listener.onDragFinished() - } - } -} diff --git a/app/src/main/java/com/idormy/sms/forwarder/adapter/base/ItemMoveCallback.kt b/app/src/main/java/com/idormy/sms/forwarder/adapter/base/ItemMoveCallback.kt new file mode 100644 index 00000000..f225021c --- /dev/null +++ b/app/src/main/java/com/idormy/sms/forwarder/adapter/base/ItemMoveCallback.kt @@ -0,0 +1,34 @@ +package com.idormy.sms.forwarder.adapter.base + +import androidx.recyclerview.widget.ItemTouchHelper +import androidx.recyclerview.widget.RecyclerView + +@Suppress("DEPRECATION") +class ItemMoveCallback(private val listener: Listener) : ItemTouchHelper.Callback() { + + interface Listener { + fun onItemMove(fromPosition: Int, toPosition: Int) + fun onDragFinished() + } + + override fun getMovementFlags(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder): Int { + val dragFlags = ItemTouchHelper.UP or ItemTouchHelper.DOWN + return makeMovementFlags(dragFlags, 0) + } + + override fun onMove(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder, target: RecyclerView.ViewHolder): Boolean { + listener.onItemMove(viewHolder.adapterPosition, target.adapterPosition) + return true + } + + override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) { + // Swiping is not needed for this example + } + + override fun onSelectedChanged(viewHolder: RecyclerView.ViewHolder?, actionState: Int) { + super.onSelectedChanged(viewHolder, actionState) + if (actionState == ItemTouchHelper.ACTION_STATE_IDLE) { + listener.onDragFinished() + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/idormy/sms/forwarder/adapter/spinner/FrpcSpinnerAdapter.kt b/app/src/main/java/com/idormy/sms/forwarder/adapter/spinner/FrpcSpinnerAdapter.kt index be039dbb..9f1ea6e2 100644 --- a/app/src/main/java/com/idormy/sms/forwarder/adapter/spinner/FrpcSpinnerAdapter.kt +++ b/app/src/main/java/com/idormy/sms/forwarder/adapter/spinner/FrpcSpinnerAdapter.kt @@ -71,7 +71,7 @@ class FrpcSpinnerAdapter : BaseEditSpinnerAdapter, EditSpinnerFilter { } else { holder = convertView.tag as ViewHolder } - val item = CollectionUtils.getListItem(mDataSource, mIndexs[position]) as FrpcAdapterItem + val item = CollectionUtils.getListItem(mDataSource, mIndexs[position]) as FrpcSpinnerItem holder.iconView.setImageDrawable(item.icon) holder.statusView.setImageDrawable( getDrawable( diff --git a/app/src/main/java/com/idormy/sms/forwarder/adapter/spinner/FrpcAdapterItem.kt b/app/src/main/java/com/idormy/sms/forwarder/adapter/spinner/FrpcSpinnerItem.kt similarity index 59% rename from app/src/main/java/com/idormy/sms/forwarder/adapter/spinner/FrpcAdapterItem.kt rename to app/src/main/java/com/idormy/sms/forwarder/adapter/spinner/FrpcSpinnerItem.kt index a47aa4de..5ea8632b 100644 --- a/app/src/main/java/com/idormy/sms/forwarder/adapter/spinner/FrpcAdapterItem.kt +++ b/app/src/main/java/com/idormy/sms/forwarder/adapter/spinner/FrpcSpinnerItem.kt @@ -3,29 +3,29 @@ package com.idormy.sms.forwarder.adapter.spinner import android.graphics.drawable.Drawable @Suppress("unused") -class FrpcAdapterItem( +class FrpcSpinnerItem( var title: CharSequence, var icon: Drawable? = null, var uid: String = "", var autorun: Int? = 1 ) { - fun setTitle(title: CharSequence): FrpcAdapterItem { + fun setTitle(title: CharSequence): FrpcSpinnerItem { this.title = title return this } - fun setIcon(icon: Drawable?): FrpcAdapterItem { + fun setIcon(icon: Drawable?): FrpcSpinnerItem { this.icon = icon return this } - fun setUid(uid: String): FrpcAdapterItem { + fun setUid(uid: String): FrpcSpinnerItem { this.uid = uid return this } - fun setAutorun(autorun: Int): FrpcAdapterItem { + fun setAutorun(autorun: Int): FrpcSpinnerItem { this.autorun = autorun return this } @@ -37,13 +37,13 @@ class FrpcAdapterItem( companion object { @JvmStatic - fun of(title: CharSequence): FrpcAdapterItem { - return FrpcAdapterItem(title) + fun of(title: CharSequence): FrpcSpinnerItem { + return FrpcSpinnerItem(title) } @JvmStatic - fun arrayOf(vararg titles: CharSequence): Array { - return titles.map { FrpcAdapterItem(it) }.toTypedArray() + fun arrayOf(vararg titles: CharSequence): Array { + return titles.map { FrpcSpinnerItem(it) }.toTypedArray() } } } diff --git a/app/src/main/java/com/idormy/sms/forwarder/adapter/spinner/RuleSpinnerAdapter.kt b/app/src/main/java/com/idormy/sms/forwarder/adapter/spinner/RuleSpinnerAdapter.kt index 74def943..aea945d3 100644 --- a/app/src/main/java/com/idormy/sms/forwarder/adapter/spinner/RuleSpinnerAdapter.kt +++ b/app/src/main/java/com/idormy/sms/forwarder/adapter/spinner/RuleSpinnerAdapter.kt @@ -71,7 +71,7 @@ class RuleSpinnerAdapter : BaseEditSpinnerAdapter, EditSpinnerFilter { } else { holder = convertView.tag as ViewHolder } - val item = CollectionUtils.getListItem(mDataSource, mIndexs[position]) as RuleAdapterItem + val item = CollectionUtils.getListItem(mDataSource, mIndexs[position]) as RuleSpinnerItem holder.iconView.setImageDrawable(item.icon) holder.statusView.setImageDrawable( getDrawable( diff --git a/app/src/main/java/com/idormy/sms/forwarder/adapter/spinner/RuleAdapterItem.kt b/app/src/main/java/com/idormy/sms/forwarder/adapter/spinner/RuleSpinnerItem.kt similarity index 59% rename from app/src/main/java/com/idormy/sms/forwarder/adapter/spinner/RuleAdapterItem.kt rename to app/src/main/java/com/idormy/sms/forwarder/adapter/spinner/RuleSpinnerItem.kt index dc3ff5e7..f6a98681 100644 --- a/app/src/main/java/com/idormy/sms/forwarder/adapter/spinner/RuleAdapterItem.kt +++ b/app/src/main/java/com/idormy/sms/forwarder/adapter/spinner/RuleSpinnerItem.kt @@ -3,29 +3,29 @@ package com.idormy.sms.forwarder.adapter.spinner import android.graphics.drawable.Drawable @Suppress("unused") -class RuleAdapterItem( +class RuleSpinnerItem( var title: CharSequence, var icon: Drawable? = null, var id: Long? = 0L, var status: Int? = 1 ) { - fun setTitle(title: CharSequence): RuleAdapterItem { + fun setTitle(title: CharSequence): RuleSpinnerItem { this.title = title return this } - fun setIcon(icon: Drawable?): RuleAdapterItem { + fun setIcon(icon: Drawable?): RuleSpinnerItem { this.icon = icon return this } - fun setId(id: Long): RuleAdapterItem { + fun setId(id: Long): RuleSpinnerItem { this.id = id return this } - fun setStatus(status: Int): RuleAdapterItem { + fun setStatus(status: Int): RuleSpinnerItem { this.status = status return this } @@ -37,13 +37,13 @@ class RuleAdapterItem( companion object { @JvmStatic - fun of(title: CharSequence): RuleAdapterItem { - return RuleAdapterItem(title) + fun of(title: CharSequence): RuleSpinnerItem { + return RuleSpinnerItem(title) } @JvmStatic - fun arrayOf(vararg titles: CharSequence): Array { - return titles.map { RuleAdapterItem(it) }.toTypedArray() + fun arrayOf(vararg titles: CharSequence): Array { + return titles.map { RuleSpinnerItem(it) }.toTypedArray() } } } diff --git a/app/src/main/java/com/idormy/sms/forwarder/adapter/spinner/SenderSpinnerAdapter.kt b/app/src/main/java/com/idormy/sms/forwarder/adapter/spinner/SenderSpinnerAdapter.kt index ea3af577..45f88fb9 100644 --- a/app/src/main/java/com/idormy/sms/forwarder/adapter/spinner/SenderSpinnerAdapter.kt +++ b/app/src/main/java/com/idormy/sms/forwarder/adapter/spinner/SenderSpinnerAdapter.kt @@ -71,7 +71,7 @@ class SenderSpinnerAdapter : BaseEditSpinnerAdapter, EditSpinnerFilter { } else { holder = convertView.tag as ViewHolder } - val item = CollectionUtils.getListItem(mDataSource, mIndexs[position]) as SenderAdapterItem + val item = CollectionUtils.getListItem(mDataSource, mIndexs[position]) as SenderSpinnerItem holder.iconView.setImageDrawable(item.icon) holder.statusView.setImageDrawable( getDrawable( diff --git a/app/src/main/java/com/idormy/sms/forwarder/adapter/spinner/SenderAdapterItem.kt b/app/src/main/java/com/idormy/sms/forwarder/adapter/spinner/SenderSpinnerItem.kt similarity index 58% rename from app/src/main/java/com/idormy/sms/forwarder/adapter/spinner/SenderAdapterItem.kt rename to app/src/main/java/com/idormy/sms/forwarder/adapter/spinner/SenderSpinnerItem.kt index de23a286..ca838c87 100644 --- a/app/src/main/java/com/idormy/sms/forwarder/adapter/spinner/SenderAdapterItem.kt +++ b/app/src/main/java/com/idormy/sms/forwarder/adapter/spinner/SenderSpinnerItem.kt @@ -3,29 +3,29 @@ package com.idormy.sms.forwarder.adapter.spinner import android.graphics.drawable.Drawable @Suppress("unused") -class SenderAdapterItem( +class SenderSpinnerItem( var title: CharSequence, var icon: Drawable? = null, var id: Long? = 0L, var status: Int? = 1 ) { - fun setTitle(title: CharSequence): SenderAdapterItem { + fun setTitle(title: CharSequence): SenderSpinnerItem { this.title = title return this } - fun setIcon(icon: Drawable?): SenderAdapterItem { + fun setIcon(icon: Drawable?): SenderSpinnerItem { this.icon = icon return this } - fun setId(id: Long): SenderAdapterItem { + fun setId(id: Long): SenderSpinnerItem { this.id = id return this } - fun setStatus(status: Int): SenderAdapterItem { + fun setStatus(status: Int): SenderSpinnerItem { this.status = status return this } @@ -37,13 +37,13 @@ class SenderAdapterItem( companion object { @JvmStatic - fun of(title: CharSequence): SenderAdapterItem { - return SenderAdapterItem(title) + fun of(title: CharSequence): SenderSpinnerItem { + return SenderSpinnerItem(title) } @JvmStatic - fun arrayOf(vararg titles: CharSequence): Array { - return titles.map { SenderAdapterItem(it) }.toTypedArray() + fun arrayOf(vararg titles: CharSequence): Array { + return titles.map { SenderSpinnerItem(it) }.toTypedArray() } } } diff --git a/app/src/main/java/com/idormy/sms/forwarder/database/dao/FrpcDao.kt b/app/src/main/java/com/idormy/sms/forwarder/database/dao/FrpcDao.kt index 3987e30b..0ced8bd7 100644 --- a/app/src/main/java/com/idormy/sms/forwarder/database/dao/FrpcDao.kt +++ b/app/src/main/java/com/idormy/sms/forwarder/database/dao/FrpcDao.kt @@ -40,8 +40,9 @@ interface FrpcDao { @Query("SELECT * FROM Frpc where autorun=1") fun getAutorun(): List - @Query("SELECT * FROM Frpc WHERE uid IN (:uids)") - fun getByUids(uids: List): List + //使用 ORDER BY 子句和 instr() 函数按照列表中 uid 的顺序返回结果 + @Query("SELECT * FROM Frpc WHERE uid IN (:uids) ORDER BY instr(:instr, uid)") + fun getByUids(uids: List, instr: String): List @Query("SELECT * FROM Frpc ORDER BY time DESC") fun pagingSource(): PagingSource diff --git a/app/src/main/java/com/idormy/sms/forwarder/database/dao/SenderDao.kt b/app/src/main/java/com/idormy/sms/forwarder/database/dao/SenderDao.kt index 93a372f3..bd96e02d 100644 --- a/app/src/main/java/com/idormy/sms/forwarder/database/dao/SenderDao.kt +++ b/app/src/main/java/com/idormy/sms/forwarder/database/dao/SenderDao.kt @@ -41,8 +41,9 @@ interface SenderDao { @Query("SELECT * FROM Sender where id=:id") fun getOne(id: Long): Sender - @Query("SELECT * FROM Sender WHERE id IN (:ids)") - fun getByIds(ids: List): List + //使用 ORDER BY 子句和 instr() 函数按照列表中 ID 的顺序返回结果 + @Query("SELECT * FROM Sender WHERE id IN (:ids) ORDER BY instr(:instr, id)") + fun getByIds(ids: List, instr: String): List @Query("SELECT count(*) FROM Sender where type=:type and status=:status") fun count(type: String, status: Int): Single diff --git a/app/src/main/java/com/idormy/sms/forwarder/database/ext/ConvertersSenderList.kt b/app/src/main/java/com/idormy/sms/forwarder/database/ext/ConvertersSenderList.kt index 7a270a21..463285a1 100644 --- a/app/src/main/java/com/idormy/sms/forwarder/database/ext/ConvertersSenderList.kt +++ b/app/src/main/java/com/idormy/sms/forwarder/database/ext/ConvertersSenderList.kt @@ -8,7 +8,7 @@ class ConvertersSenderList { @TypeConverter fun stringToObject(value: String): List { - return Core.sender.getByIds(value.split(",").map { it.trim().toLong() }) + return Core.sender.getByIds(value.split(",").map { it.trim().toLong() }, value) } @TypeConverter diff --git a/app/src/main/java/com/idormy/sms/forwarder/database/repository/FrpcRepository.kt b/app/src/main/java/com/idormy/sms/forwarder/database/repository/FrpcRepository.kt index 92539d14..b38a517d 100644 --- a/app/src/main/java/com/idormy/sms/forwarder/database/repository/FrpcRepository.kt +++ b/app/src/main/java/com/idormy/sms/forwarder/database/repository/FrpcRepository.kt @@ -31,6 +31,6 @@ class FrpcRepository(private val frpcDao: FrpcDao) { fun getAutorun(): List = frpcDao.getAutorun() - fun getByUids(uids: List): List = frpcDao.getByUids(uids) + fun getByUids(uids: List, instr: String): List = frpcDao.getByUids(uids, instr) } \ No newline at end of file diff --git a/app/src/main/java/com/idormy/sms/forwarder/database/repository/SenderRepository.kt b/app/src/main/java/com/idormy/sms/forwarder/database/repository/SenderRepository.kt index ddcd0248..56e72183 100644 --- a/app/src/main/java/com/idormy/sms/forwarder/database/repository/SenderRepository.kt +++ b/app/src/main/java/com/idormy/sms/forwarder/database/repository/SenderRepository.kt @@ -30,7 +30,7 @@ class SenderRepository(private val senderDao: SenderDao) { fun getOne(id: Long) = senderDao.getOne(id) - fun getByIds(ids: List) = senderDao.getByIds(ids) + fun getByIds(ids: List, instr: String) = senderDao.getByIds(ids, instr) fun getAllNonCache(): List { val query = SimpleSQLiteQuery("SELECT * FROM Sender ORDER BY id ASC") diff --git a/app/src/main/java/com/idormy/sms/forwarder/fragment/RulesEditFragment.kt b/app/src/main/java/com/idormy/sms/forwarder/fragment/RulesEditFragment.kt index 62128e8c..001a7599 100644 --- a/app/src/main/java/com/idormy/sms/forwarder/fragment/RulesEditFragment.kt +++ b/app/src/main/java/com/idormy/sms/forwarder/fragment/RulesEditFragment.kt @@ -7,14 +7,19 @@ import android.view.ViewGroup import android.widget.* import androidx.fragment.app.viewModels import androidx.lifecycle.Observer +import androidx.recyclerview.widget.ItemTouchHelper +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView import androidx.work.OneTimeWorkRequestBuilder import androidx.work.WorkManager import com.idormy.sms.forwarder.App import com.idormy.sms.forwarder.R +import com.idormy.sms.forwarder.adapter.SenderRecyclerAdapter +import com.idormy.sms.forwarder.adapter.base.ItemMoveCallback import com.idormy.sms.forwarder.adapter.spinner.AppListAdapterItem import com.idormy.sms.forwarder.adapter.spinner.AppListSpinnerAdapter -import com.idormy.sms.forwarder.adapter.spinner.SenderAdapterItem import com.idormy.sms.forwarder.adapter.spinner.SenderSpinnerAdapter +import com.idormy.sms.forwarder.adapter.spinner.SenderSpinnerItem import com.idormy.sms.forwarder.core.BaseFragment import com.idormy.sms.forwarder.core.Core import com.idormy.sms.forwarder.database.entity.Rule @@ -74,16 +79,17 @@ class RulesEditFragment : BaseFragment(), View.OnClic private var silentPeriodStart = 0 private var silentPeriodEnd = 0 - //当前发送通道 - private var senderId = 0L - private var senderListSelected: MutableList = mutableListOf() - private var senderItemMap = HashMap(2) - - //发送通道列表 - private var senderListAll: MutableList = mutableListOf() - private val senderSpinnerList = ArrayList() + //所有发送通道下拉框 + private var senderListAll = mutableListOf() + private val senderSpinnerList = mutableListOf() private lateinit var senderSpinnerAdapter: SenderSpinnerAdapter<*> + //已选发送通道列表 + private var senderId = 0L + private var senderListSelected = mutableListOf() + private lateinit var senderRecyclerView: RecyclerView + private lateinit var senderRecyclerAdapter: SenderRecyclerAdapter + //已安装App信息列表 private val appListSpinnerList = ArrayList() private lateinit var appListSpinnerAdapter: AppListSpinnerAdapter<*> @@ -400,49 +406,16 @@ class RulesEditFragment : BaseFragment(), View.OnClic } //初始化发送通道下拉框 - @SuppressLint("SetTextI18n") + @SuppressLint("SetTextI18n", "NotifyDataSetChanged") private fun initSenderSpinner() { //免打扰(禁用转发)时间段 binding!!.tvSilentPeriod.text = mTimeOption[silentPeriodStart] + " ~ " + mTimeOption[silentPeriodEnd] - Core.sender.getAll().subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(object : SingleObserver> { - override fun onSubscribe(d: Disposable) {} - - override fun onError(e: Throwable) { - e.printStackTrace() - Log.e(TAG, e.toString()) - } - - override fun onSuccess(senderList: List) { - if (senderList.isEmpty()) { - XToastUtils.error(R.string.add_sender_first) - return - } - - senderListAll = senderList as MutableList - for (sender in senderList) { - val name = if (sender.name.length > 20) sender.name.substring(0, 19) else sender.name - senderSpinnerList.add(SenderAdapterItem(name, getDrawable(sender.imageId), sender.id, sender.status)) - } - senderSpinnerAdapter = SenderSpinnerAdapter(senderSpinnerList) - .setIsFilterKey(true).setFilterColor("#EF5362").setBackgroundSelector(R.drawable.selector_custom_spinner_bg) - binding!!.spSender.setAdapter(senderSpinnerAdapter) - - if (senderListSelected.isNotEmpty()) { - for (sender in senderListSelected) { - for (senderItem in senderSpinnerList) { - if (sender.id == senderItem.id) { - addSenderItemLinearLayout(senderItemMap, binding!!.layoutSenders, senderItem) - } - } - } - } - } - }) + //初始化发送通道下拉框 binding!!.spSender.setOnItemClickListener { _: AdapterView<*>, _: View, position: Int, _: Long -> try { - val sender = senderSpinnerAdapter.getItemSource(position) as SenderAdapterItem - senderId = sender.id!! + val item = senderSpinnerAdapter.getItemSource(position) as SenderSpinnerItem + senderId = item.id!! if (senderId > 0L) { senderListSelected.forEach { if (senderId == it.id) { @@ -453,11 +426,14 @@ class RulesEditFragment : BaseFragment(), View.OnClic senderListAll.forEach { if (senderId == it.id) { senderListSelected.add(it) - addSenderItemLinearLayout(senderItemMap, binding!!.layoutSenders, sender) } } - if (STATUS_OFF == sender.status) { + checkSenderLogicShow() + + senderRecyclerAdapter.notifyDataSetChanged() + + if (STATUS_OFF == item.status) { XToastUtils.warning(getString(R.string.sender_disabled_tips)) } } @@ -465,60 +441,80 @@ class RulesEditFragment : BaseFragment(), View.OnClic XToastUtils.error(e.message.toString()) } } + + // 初始化已选发送通道列表 RecyclerView 和 Adapter + senderRecyclerView = binding!!.recyclerSenders + senderRecyclerAdapter = SenderRecyclerAdapter(senderListSelected, { position -> + senderListSelected.removeAt(position) + senderRecyclerAdapter.notifyItemRemoved(position) + senderRecyclerAdapter.notifyItemRangeChanged(position, senderListSelected.size) // 更新索引 + checkSenderLogicShow() + }) + senderRecyclerView.apply { + layoutManager = LinearLayoutManager(requireContext()) + adapter = senderRecyclerAdapter + } + val senderMoveCallback = ItemMoveCallback(object : ItemMoveCallback.Listener { + override fun onItemMove(fromPosition: Int, toPosition: Int) { + Log.d(TAG, "onItemMove: $fromPosition $toPosition") + senderRecyclerAdapter.onItemMove(fromPosition, toPosition) + senderListSelected = senderRecyclerAdapter.itemList + } + + override fun onDragFinished() { + senderListSelected = senderRecyclerAdapter.itemList + //senderRecyclerAdapter.notifyDataSetChanged() + Log.d(TAG, "onDragFinished: $senderListSelected") + } + }) + val senderTouchHelper = ItemTouchHelper(senderMoveCallback) + senderTouchHelper.attachToRecyclerView(senderRecyclerView) + senderRecyclerAdapter.setTouchHelper(senderTouchHelper) + + //获取发送通道列表 + getSenderList() } - /** - * 动态增删Sender - * - * @param senderItemMap 管理item的map,用于删除指定header - * @param layoutSenders 需要挂载item的LinearLayout - * @param sender SenderAdapterItem - */ - @SuppressLint("SetTextI18n") - private fun addSenderItemLinearLayout( - senderItemMap: MutableMap, layoutSenders: LinearLayout, sender: SenderAdapterItem - ) { - val layoutSenderItem = View.inflate(requireContext(), R.layout.item_add_sender, null) as LinearLayout - val ivRemoveSender = layoutSenderItem.findViewById(R.id.iv_remove_sender) - val ivSenderImage = layoutSenderItem.findViewById(R.id.iv_sender_image) - val ivSenderStatus = layoutSenderItem.findViewById(R.id.iv_sender_status) - val tvSenderName = layoutSenderItem.findViewById(R.id.tv_sender_name) + //获取发送通道列表 + private fun getSenderList() { + Core.sender.getAll().subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(object : SingleObserver> { + override fun onSubscribe(d: Disposable) {} - ivSenderImage.setImageDrawable(sender.icon) - ivSenderStatus.setImageDrawable(getDrawable(if (STATUS_OFF == sender.status) R.drawable.ic_stop else R.drawable.ic_start)) - val senderItemId = sender.id as Long - tvSenderName.text = "ID-$senderItemId:${sender.title}" + override fun onError(e: Throwable) { + e.printStackTrace() + Log.e(TAG, "getSenderList error: ${e.message}") + } - ivRemoveSender.tag = senderItemId - ivRemoveSender.setOnClickListener { view2: View -> - val tagId = view2.tag as Long - layoutSenders.removeView(senderItemMap[tagId]) - senderItemMap.remove(tagId) - //senderListSelected.removeIf { it.id == tagId } - for (it in senderListSelected) { - if (it.id == tagId) { - senderListSelected -= it - break + @SuppressLint("NotifyDataSetChanged") + override fun onSuccess(senderList: List) { + if (senderList.isEmpty()) { + XToastUtils.error(R.string.add_sender_first) + return } + + senderSpinnerList.clear() + senderListAll = senderList as MutableList + for (sender in senderList) { + val name = if (sender.name.length > 20) sender.name.substring(0, 19) else sender.name + senderSpinnerList.add(SenderSpinnerItem(name, getDrawable(sender.imageId), sender.id, sender.status)) + } + senderSpinnerAdapter = SenderSpinnerAdapter(senderSpinnerList).setIsFilterKey(true).setFilterColor("#EF5362").setBackgroundSelector(R.drawable.selector_custom_spinner_bg) + binding!!.spSender.setAdapter(senderSpinnerAdapter) + //senderSpinnerAdapter.notifyDataSetChanged() + + //更新senderListSelected的状态与名称 + senderListSelected.forEach { + senderListAll.forEach { sender -> + if (it.id == sender.id) { + it.name = sender.name + it.status = sender.status + } + } + } + senderRecyclerAdapter.notifyDataSetChanged() + } - Log.d(TAG, senderListSelected.count().toString()) - Log.d(TAG, senderListSelected.toString()) - if (senderListSelected.isEmpty()) senderId = 0L - if (senderListSelected.count() > 1) { - binding!!.layoutSenderLogic.visibility = View.VISIBLE - } else { - binding!!.layoutSenderLogic.visibility = View.GONE - binding!!.rgSenderLogic.check(R.id.rb_sender_logic_all) - } - } - layoutSenders.addView(layoutSenderItem) - senderItemMap[senderItemId] = layoutSenderItem - if (senderListSelected.count() > 1) { - binding!!.layoutSenderLogic.visibility = View.VISIBLE - } else { - binding!!.layoutSenderLogic.visibility = View.GONE - binding!!.rgSenderLogic.check(R.id.rb_sender_logic_all) - } + }) } //初始化APP下拉列表 @@ -581,6 +577,7 @@ class RulesEditFragment : BaseFragment(), View.OnClic rule.senderList.forEach { senderId = it.id senderListSelected.add(it) + Log.d(TAG, it.toString()) } if (isClone) { @@ -591,6 +588,7 @@ class RulesEditFragment : BaseFragment(), View.OnClic } Log.d(TAG, rule.toString()) + checkSenderLogicShow() binding!!.rgSenderLogic.check(rule.getSenderLogicCheckId()) binding!!.rgSimSlot.check(rule.getSimSlotCheckId()) binding!!.rgFiled.check(rule.getFiledCheckId()) @@ -619,6 +617,15 @@ class RulesEditFragment : BaseFragment(), View.OnClic }) } + private fun checkSenderLogicShow() { + if (senderListSelected.size > 1) { + binding!!.layoutSenderLogic.visibility = View.VISIBLE + } else { + binding!!.layoutSenderLogic.visibility = View.GONE + binding!!.rgSenderLogic.check(R.id.rb_sender_logic_all) + } + } + //提交前检查表单 private fun checkForm(): Rule { if (senderListSelected.isEmpty() || senderId == 0L) { @@ -677,9 +684,7 @@ class RulesEditFragment : BaseFragment(), View.OnClic else -> CHECK_SIM_SLOT_ALL } val status = if (binding!!.sbStatus.isChecked) STATUS_ON else STATUS_OFF - //if (status == STATUS_OFF) { - // throw Exception(getString(R.string.invalid_rule_status)) - //} + return Rule( ruleId, ruleType, diff --git a/app/src/main/java/com/idormy/sms/forwarder/fragment/TasksEditFragment.kt b/app/src/main/java/com/idormy/sms/forwarder/fragment/TasksEditFragment.kt index d445c53f..d3928c2a 100644 --- a/app/src/main/java/com/idormy/sms/forwarder/fragment/TasksEditFragment.kt +++ b/app/src/main/java/com/idormy/sms/forwarder/fragment/TasksEditFragment.kt @@ -13,9 +13,9 @@ import com.google.android.material.bottomsheet.BottomSheetDialog import com.google.gson.Gson import com.idormy.sms.forwarder.App import com.idormy.sms.forwarder.R -import com.idormy.sms.forwarder.adapter.ItemMoveCallback import com.idormy.sms.forwarder.adapter.TaskSettingAdapter import com.idormy.sms.forwarder.adapter.WidgetItemAdapter +import com.idormy.sms.forwarder.adapter.base.ItemMoveCallback import com.idormy.sms.forwarder.core.BaseFragment import com.idormy.sms.forwarder.core.Core import com.idormy.sms.forwarder.database.entity.Task @@ -50,7 +50,7 @@ import java.util.* @Page(name = "自动任务·编辑器") -@Suppress("PrivatePropertyName", "DEPRECATION", "UNUSED_PARAMETER", "EmptyMethod") +@Suppress("PrivatePropertyName", "DEPRECATION", "UNUSED_PARAMETER", "EmptyMethod", "NotifyDataSetChanged") class TasksEditFragment : BaseFragment(), View.OnClickListener, RecyclerViewHolder.OnItemClickListener { private val TAG: String = TasksEditFragment::class.java.simpleName @@ -70,14 +70,13 @@ class TasksEditFragment : BaseFragment(), View.OnClic @AutoWired(name = KEY_TASK_CLONE) var isClone: Boolean = false - private lateinit var recyclerConditions: RecyclerView - private lateinit var recyclerActions: RecyclerView - + private lateinit var conditionsRecyclerView: RecyclerView private lateinit var conditionsAdapter: TaskSettingAdapter - private lateinit var actionsAdapter: TaskSettingAdapter + private var conditionsList = mutableListOf() - private var itemListConditions = mutableListOf() - private var itemListActions = mutableListOf() + private lateinit var actionsRecyclerView: RecyclerView + private lateinit var actionsAdapter: TaskSettingAdapter + private var actionsList = mutableListOf() private var TASK_CONDITION_FRAGMENT_LIST = listOf( PageInfo( @@ -218,8 +217,8 @@ class TasksEditFragment : BaseFragment(), View.OnClic * 初始化控件 */ override fun initViews() { - recyclerConditions = findViewById(R.id.recycler_conditions) - recyclerActions = findViewById(R.id.recycler_actions) + conditionsRecyclerView = findViewById(R.id.recycler_conditions) + actionsRecyclerView = findViewById(R.id.recycler_actions) // 初始化 RecyclerView 和 Adapter initRecyclerViews() @@ -229,34 +228,38 @@ class TasksEditFragment : BaseFragment(), View.OnClic override fun onItemMove(fromPosition: Int, toPosition: Int) { Log.d(TAG, "onItemMove: $fromPosition $toPosition") conditionsAdapter.onItemMove(fromPosition, toPosition) + conditionsList = conditionsAdapter.itemList } override fun onDragFinished() { - //itemListConditions保持与adapter一致 - itemListConditions = conditionsAdapter.itemList.toMutableList() - Log.d(TAG, "onItemMove: $itemListConditions") + //conditionsList保持与adapter一致 + conditionsList = conditionsAdapter.itemList + Log.d(TAG, "onDragFinished: $conditionsList") + //conditionsAdapter.notifyDataSetChanged() } }) val itemTouchHelperConditions = ItemTouchHelper(conditionsCallback) - itemTouchHelperConditions.attachToRecyclerView(recyclerConditions) + itemTouchHelperConditions.attachToRecyclerView(conditionsRecyclerView) conditionsAdapter.setTouchHelper(itemTouchHelperConditions) val actionsCallback = ItemMoveCallback(object : ItemMoveCallback.Listener { override fun onItemMove(fromPosition: Int, toPosition: Int) { Log.d(TAG, "onItemMove: $fromPosition $toPosition") actionsAdapter.onItemMove(fromPosition, toPosition) + actionsList = actionsAdapter.itemList } override fun onDragFinished() { - //itemListActions保持与adapter一致 - itemListActions = actionsAdapter.itemList.toMutableList() - Log.d(TAG, "onItemMove: $itemListActions") + //actionsList保持与adapter一致 + actionsList = actionsAdapter.itemList + Log.d(TAG, "onDragFinished: $actionsList") + //actionsAdapter.notifyDataSetChanged() } }) val itemTouchHelperActions = ItemTouchHelper(actionsCallback) - itemTouchHelperActions.attachToRecyclerView(recyclerActions) + itemTouchHelperActions.attachToRecyclerView(actionsRecyclerView) actionsAdapter.setTouchHelper(itemTouchHelperActions) if (taskId <= 0) { //新增 @@ -284,7 +287,7 @@ class TasksEditFragment : BaseFragment(), View.OnClic R.id.layout_add_condition -> { val bottomSheet: View = LayoutInflater.from(requireContext()).inflate(R.layout.dialog_task_condition_bottom_sheet, null) val tvTitle: TextView = bottomSheet.findViewById(R.id.tv_title) - tvTitle.text = if (itemListConditions.isEmpty()) getString(R.string.select_task_trigger) else getString(R.string.select_task_condition) + tvTitle.text = if (conditionsList.isEmpty()) getString(R.string.select_task_trigger) else getString(R.string.select_task_condition) val recyclerView: RecyclerView = bottomSheet.findViewById(R.id.recyclerView) WidgetUtils.initGridRecyclerView(recyclerView, 4, DensityUtils.dp2px(1f)) @@ -386,20 +389,20 @@ class TasksEditFragment : BaseFragment(), View.OnClic if (task.conditions.isNotEmpty()) { val conditionList = Gson().fromJson(task.conditions, Array::class.java).toMutableList() for (condition in conditionList) { - itemListConditions.add(condition) + this@TasksEditFragment.conditionsList.add(condition) } - Log.d(TAG, "itemListConditions: $itemListConditions") + Log.d(TAG, "conditionsList: ${this@TasksEditFragment.conditionsList}") conditionsAdapter.notifyDataSetChanged() - binding!!.layoutAddCondition.visibility = if (itemListConditions.size >= MAX_SETTING_NUM) View.GONE else View.VISIBLE + binding!!.layoutAddCondition.visibility = if (this@TasksEditFragment.conditionsList.size >= MAX_SETTING_NUM) View.GONE else View.VISIBLE } if (task.actions.isNotEmpty()) { val actionList = Gson().fromJson(task.actions, Array::class.java).toMutableList() for (action in actionList) { - itemListActions.add(action) + this@TasksEditFragment.actionsList.add(action) } - Log.d(TAG, "itemListActions: $itemListActions") + Log.d(TAG, "actionsList: ${this@TasksEditFragment.actionsList}") actionsAdapter.notifyDataSetChanged() - binding!!.layoutAddAction.visibility = if (itemListActions.size >= MAX_SETTING_NUM) View.GONE else View.VISIBLE + binding!!.layoutAddAction.visibility = if (this@TasksEditFragment.actionsList.size >= MAX_SETTING_NUM) View.GONE else View.VISIBLE } } catch (e: Exception) { e.printStackTrace() @@ -416,10 +419,10 @@ class TasksEditFragment : BaseFragment(), View.OnClic if (taskName.isEmpty()) { throw Exception("请输入任务名称") } - if (itemListConditions.size <= 0) { + if (conditionsList.size <= 0) { throw Exception("请添加触发条件") } - if (itemListActions.size <= 0) { + if (actionsList.size <= 0) { throw Exception("请添加执行动作") } @@ -427,7 +430,7 @@ class TasksEditFragment : BaseFragment(), View.OnClic // 将毫秒部分设置为 0,避免因为毫秒部分不同导致的任务重复执行 lastExecTime.time = lastExecTime.time / 1000 * 1000 var nextExecTime = lastExecTime - val firstCondition = itemListConditions[0] + val firstCondition = conditionsList[0] taskType = firstCondition.type when (taskType) { @@ -445,13 +448,13 @@ class TasksEditFragment : BaseFragment(), View.OnClic //拼接任务描述 val description = StringBuilder() description.append(getString(R.string.task_conditions)).append(" ") - description.append(itemListConditions.map { it.description }.toTypedArray().joinToString(",")) + description.append(conditionsList.map { it.description }.toTypedArray().joinToString(",")) description.append(" ").append(getString(R.string.task_actions)).append(" ") - description.append(itemListActions.map { it.description }.toTypedArray().joinToString(",")) + description.append(actionsList.map { it.description }.toTypedArray().joinToString(",")) val status = if (binding!!.sbStatus.isChecked) STATUS_ON else STATUS_OFF return Task( - taskId, taskType, taskName, description.toString(), Gson().toJson(itemListConditions), Gson().toJson(itemListActions), status, lastExecTime, nextExecTime + taskId, taskType, taskName, description.toString(), Gson().toJson(conditionsList), Gson().toJson(actionsList), status, lastExecTime, nextExecTime ) } @@ -481,7 +484,7 @@ class TasksEditFragment : BaseFragment(), View.OnClic if (widgetInfo.classPath.contains(".condition.")) { val typeCondition = pos + KEY_BACK_CODE_CONDITION //判断是否已经添加过该类型条件 - for (item in itemListConditions) { + for (item in conditionsList) { //注意:TASK_CONDITION_XXX 枚举值 等于 TASK_CONDITION_FRAGMENT_LIST 索引加上 KEY_BACK_CODE_CONDITION,不可改变 if (item.type == typeCondition) { XToastUtils.error(getString(R.string.condition_already_exists)) @@ -514,7 +517,7 @@ class TasksEditFragment : BaseFragment(), View.OnClic } else { val typeAction = pos + KEY_BACK_CODE_ACTION //判断是否已经添加过该类型动作 - for (item in itemListActions) { + for (item in actionsList) { //注意:TASK_ACTION_XXX 枚举值 等于 TASK_ACTION_FRAGMENT_LIST 索引加上 KEY_BACK_CODE_ACTION,不可改变 if (item.type == typeAction) { XToastUtils.error(getString(R.string.action_already_exists)) @@ -549,15 +552,17 @@ class TasksEditFragment : BaseFragment(), View.OnClic val widgetInfo = TASK_CONDITION_FRAGMENT_LIST[widgetInfoIndex] description = extras.getString(KEY_BACK_DESCRIPTION_CONDITION) ?: widgetInfo.name.toString() val taskSetting = TaskSetting(resultCode, widgetInfo.name, description, setting, requestCode) - //requestCode: 等于 itemListConditions 的索引加1 + //requestCode: 等于 conditionsList 的索引加1 if (requestCode == 0) { - taskSetting.position = itemListConditions.size - itemListConditions.add(taskSetting) + taskSetting.position = conditionsList.size + conditionsList.add(taskSetting) + conditionsAdapter.notifyItemInserted(conditionsList.size - 1) } else { - itemListConditions[requestCode - 1] = taskSetting + conditionsList[requestCode - 1] = taskSetting + conditionsAdapter.notifyItemChanged(requestCode - 1) } - conditionsAdapter.notifyDataSetChanged() - binding!!.layoutAddCondition.visibility = if (itemListConditions.size >= MAX_SETTING_NUM) View.GONE else View.VISIBLE + //conditionsAdapter.notifyDataSetChanged() + binding!!.layoutAddCondition.visibility = if (conditionsList.size >= MAX_SETTING_NUM) View.GONE else View.VISIBLE } else if (resultCode in KEY_BACK_CODE_ACTION..KEY_BACK_CODE_ACTION + 999) { setting = extras.getString(KEY_BACK_DATA_ACTION) ?: return //注意:TASK_ACTION_XXX 枚举值 等于 TASK_ACTION_FRAGMENT_LIST 索引加上 KEY_BACK_CODE_ACTION,不可改变 @@ -566,31 +571,33 @@ class TasksEditFragment : BaseFragment(), View.OnClic val widgetInfo = TASK_ACTION_FRAGMENT_LIST[widgetInfoIndex] description = extras.getString(KEY_BACK_DESCRIPTION_ACTION) ?: widgetInfo.name.toString() val taskSetting = TaskSetting(resultCode, widgetInfo.name, description, setting, requestCode) - //requestCode: 等于 itemListActions 的索引加1 + //requestCode: 等于 actionsList 的索引加1 if (requestCode == 0) { - taskSetting.position = itemListActions.size - itemListActions.add(taskSetting) + taskSetting.position = actionsList.size + actionsList.add(taskSetting) + actionsAdapter.notifyItemInserted(actionsList.size - 1) } else { - itemListActions[requestCode - 1] = taskSetting + actionsList[requestCode - 1] = taskSetting + actionsAdapter.notifyItemChanged(requestCode - 1) } - actionsAdapter.notifyDataSetChanged() - binding!!.layoutAddAction.visibility = if (itemListActions.size >= MAX_SETTING_NUM) View.GONE else View.VISIBLE + //actionsAdapter.notifyDataSetChanged() + binding!!.layoutAddAction.visibility = if (actionsList.size >= MAX_SETTING_NUM) View.GONE else View.VISIBLE } Log.d(TAG, "requestCode:$requestCode resultCode:$resultCode setting:$setting") } } private fun initRecyclerViews() { - conditionsAdapter = TaskSettingAdapter(itemListConditions, { position -> editCondition(position) }, { position -> removeCondition(position) }) + conditionsAdapter = TaskSettingAdapter(conditionsList, { position -> removeCondition(position) }, { position -> editCondition(position) }) - actionsAdapter = TaskSettingAdapter(itemListActions, { position -> editAction(position) }, { position -> removeAction(position) }) + actionsAdapter = TaskSettingAdapter(actionsList, { position -> removeAction(position) }, { position -> editAction(position) }) - recyclerConditions.apply { + conditionsRecyclerView.apply { layoutManager = LinearLayoutManager(requireContext()) adapter = conditionsAdapter } - recyclerActions.apply { + actionsRecyclerView.apply { layoutManager = LinearLayoutManager(requireContext()) adapter = actionsAdapter } @@ -599,42 +606,48 @@ class TasksEditFragment : BaseFragment(), View.OnClic private fun editCondition(position: Int) { // 实现编辑条件项目的逻辑 // 根据 position 对特定项目进行编辑 - val condition = itemListConditions[position] + val condition = conditionsList[position] Log.d(TAG, "editCondition: $position, $condition") val widgetInfoIndex = condition.type - KEY_BACK_CODE_CONDITION //判断是否存在 if (widgetInfoIndex < 0 || widgetInfoIndex >= TASK_CONDITION_FRAGMENT_LIST.size) return val widgetInfo = TASK_CONDITION_FRAGMENT_LIST[condition.type - KEY_BACK_CODE_CONDITION] - @Suppress("UNCHECKED_CAST") PageOption.to(Class.forName(widgetInfo.classPath) as Class) //跳转的fragment - .setRequestCode(position + 1) //requestCode: 0 新增 、>0 编辑(itemListConditions 的索引加1) - .putString(KEY_EVENT_DATA_CONDITION, condition.setting).open(this) + @Suppress("UNCHECKED_CAST") + PageOption.to(Class.forName(widgetInfo.classPath) as Class) //跳转的fragment + .setRequestCode(position + 1) //requestCode: 0 新增 、>0 编辑(conditionsList 的索引加1) + .putString(KEY_EVENT_DATA_CONDITION, condition.setting) + .open(this) } private fun removeCondition(position: Int) { - itemListConditions.removeAt(position) + conditionsList.removeAt(position) conditionsAdapter.notifyItemRemoved(position) - binding!!.layoutAddCondition.visibility = if (itemListConditions.size >= MAX_SETTING_NUM) View.GONE else View.VISIBLE + conditionsAdapter.notifyItemRangeChanged(position, conditionsList.size) // 更新索引 + binding!!.layoutAddCondition.visibility = if (conditionsList.size >= MAX_SETTING_NUM) View.GONE else View.VISIBLE } private fun editAction(position: Int) { // 实现编辑操作项目的逻辑 // 根据 position 对特定项目进行编辑 - val action = itemListActions[position] + val action = actionsList[position] Log.d(TAG, "editAction: $position, $action") val widgetInfoIndex = action.type - KEY_BACK_CODE_ACTION //判断是否存在 if (widgetInfoIndex < 0 || widgetInfoIndex >= TASK_ACTION_FRAGMENT_LIST.size) return val widgetInfo = TASK_ACTION_FRAGMENT_LIST[action.type - KEY_BACK_CODE_ACTION] - @Suppress("UNCHECKED_CAST") PageOption.to(Class.forName(widgetInfo.classPath) as Class) //跳转的fragment - .setRequestCode(position + 1) //requestCode: 0 新增 、>0 编辑(itemListActions 的索引加1) - .putString(KEY_EVENT_DATA_ACTION, action.setting).open(this) + @Suppress("UNCHECKED_CAST") + PageOption.to(Class.forName(widgetInfo.classPath) as Class) //跳转的fragment + .setRequestCode(position + 1) //requestCode: 0 新增 、>0 编辑(actionsList 的索引加1) + .putString(KEY_EVENT_DATA_ACTION, action.setting) + .open(this) } private fun removeAction(position: Int) { - itemListActions.removeAt(position) + actionsList.removeAt(position) actionsAdapter.notifyItemRemoved(position) - binding!!.layoutAddAction.visibility = if (itemListActions.size >= MAX_SETTING_NUM) View.GONE else View.VISIBLE + actionsAdapter.notifyItemRangeChanged(position, actionsList.size) // 更新索引 + binding!!.layoutAddAction.visibility = if (actionsList.size >= MAX_SETTING_NUM) View.GONE else View.VISIBLE } } \ No newline at end of file diff --git a/app/src/main/java/com/idormy/sms/forwarder/fragment/action/FrpcFragment.kt b/app/src/main/java/com/idormy/sms/forwarder/fragment/action/FrpcFragment.kt index 8ffb1c0a..9da76fd1 100644 --- a/app/src/main/java/com/idormy/sms/forwarder/fragment/action/FrpcFragment.kt +++ b/app/src/main/java/com/idormy/sms/forwarder/fragment/action/FrpcFragment.kt @@ -6,13 +6,15 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.AdapterView -import android.widget.ImageView -import android.widget.LinearLayout -import android.widget.TextView +import androidx.recyclerview.widget.ItemTouchHelper +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView import com.google.gson.Gson import com.idormy.sms.forwarder.R -import com.idormy.sms.forwarder.adapter.spinner.FrpcAdapterItem +import com.idormy.sms.forwarder.adapter.FrpcRecyclerAdapter +import com.idormy.sms.forwarder.adapter.base.ItemMoveCallback import com.idormy.sms.forwarder.adapter.spinner.FrpcSpinnerAdapter +import com.idormy.sms.forwarder.adapter.spinner.FrpcSpinnerItem import com.idormy.sms.forwarder.core.BaseFragment import com.idormy.sms.forwarder.core.Core import com.idormy.sms.forwarder.database.entity.Frpc @@ -23,7 +25,6 @@ import com.idormy.sms.forwarder.utils.KEY_BACK_DESCRIPTION_ACTION import com.idormy.sms.forwarder.utils.KEY_EVENT_DATA_ACTION import com.idormy.sms.forwarder.utils.KEY_TEST_ACTION import com.idormy.sms.forwarder.utils.Log -import com.idormy.sms.forwarder.utils.STATUS_ON import com.idormy.sms.forwarder.utils.TASK_ACTION_FRPC import com.idormy.sms.forwarder.utils.XToastUtils import com.jeremyliao.liveeventbus.LiveEventBus @@ -47,16 +48,17 @@ class FrpcFragment : BaseFragment(), View.OnCli private var titleBar: TitleBar? = null private var mCountDownHelper: CountDownButtonHelper? = null - //当前发送通道 - private var frpcUid = "" - private var frpcListSelected: MutableList = mutableListOf() - private var frpcItemMap = HashMap(2) - - //发送通道列表 - private var frpcListAll: MutableList = mutableListOf() - private val frpcSpinnerList = ArrayList() + //所有Frpc下拉框 + private var frpcListAll = mutableListOf() + private val frpcSpinnerList = mutableListOf() private lateinit var frpcSpinnerAdapter: FrpcSpinnerAdapter<*> + //已选Frpc列表 + private var frpcUid = "" + private var frpcListSelected = mutableListOf() + private lateinit var frpcRecyclerView: RecyclerView + private lateinit var frpcRecyclerAdapter: FrpcRecyclerAdapter + @JvmField @AutoWired(name = KEY_EVENT_DATA_ACTION) var eventData: String? = null @@ -101,7 +103,7 @@ class FrpcFragment : BaseFragment(), View.OnCli } //初始化发送通道下拉框 - initFrpcSpinner() + initFrpc() } @SuppressLint("SetTextI18n") @@ -162,47 +164,14 @@ class FrpcFragment : BaseFragment(), View.OnCli } } - //初始化发送通道下拉框 - @SuppressLint("SetTextI18n") - private fun initFrpcSpinner() { - Core.frpc.getAll().subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(object : SingleObserver> { - override fun onSubscribe(d: Disposable) {} - - override fun onError(e: Throwable) { - e.printStackTrace() - Log.e(TAG, "initFrpcSpinner error: ${e.message}") - } - - override fun onSuccess(frpcList: List) { - if (frpcList.isEmpty()) { - XToastUtils.error(R.string.add_frpc_first) - return - } - - frpcListAll = frpcList as MutableList - for (frpc in frpcList) { - val name = if (frpc.name.length > 20) frpc.name.substring(0, 19) else frpc.name - frpcSpinnerList.add(FrpcAdapterItem(name, getDrawable(R.drawable.auto_task_icon_frpc), frpc.uid, frpc.autorun)) - } - frpcSpinnerAdapter = FrpcSpinnerAdapter(frpcSpinnerList) - .setIsFilterKey(true).setFilterColor("#EF5362").setBackgroundSelector(R.drawable.selector_custom_spinner_bg) - binding!!.spFrpc.setAdapter(frpcSpinnerAdapter) - - if (frpcListSelected.isNotEmpty()) { - for (frpc in frpcListSelected) { - for (frpcItem in frpcSpinnerList) { - if (frpc.uid == frpcItem.uid) { - addFrpcItemLinearLayout(frpcItemMap, binding!!.layoutFrpcs, frpcItem) - } - } - } - } - } - }) + //初始化Frpc + @SuppressLint("SetTextI18n", "NotifyDataSetChanged") + private fun initFrpc() { + //初始化Frpc下拉框 binding!!.spFrpc.setOnItemClickListener { _: AdapterView<*>, _: View, position: Int, _: Long -> try { - val frpc = frpcSpinnerAdapter.getItemSource(position) as FrpcAdapterItem - frpcUid = frpc.uid + val item = frpcSpinnerAdapter.getItemSource(position) as FrpcSpinnerItem + frpcUid = item.uid if (frpcUid.isNotEmpty()) { frpcListSelected.forEach { if (frpcUid == it.uid) { @@ -213,60 +182,87 @@ class FrpcFragment : BaseFragment(), View.OnCli frpcListAll.forEach { if (frpcUid == it.uid) { frpcListSelected.add(it) - addFrpcItemLinearLayout(frpcItemMap, binding!!.layoutFrpcs, frpc) } } - - /*if (STATUS_OFF == frpc.status) { - XToastUtils.warning(getString(R.string.frpc_disabled_tips)) - }*/ + frpcRecyclerAdapter.notifyDataSetChanged() } } catch (e: Exception) { XToastUtils.error(e.message.toString()) } } + + // 初始化已选Frpc列表 RecyclerView 和 Adapter + frpcRecyclerView = binding!!.recyclerFrpcs + frpcRecyclerAdapter = FrpcRecyclerAdapter(frpcListSelected, { position -> + frpcListSelected.removeAt(position) + frpcRecyclerAdapter.notifyItemRemoved(position) + frpcRecyclerAdapter.notifyItemRangeChanged(position, frpcListSelected.size) // 更新索引 + }) + frpcRecyclerView.apply { + layoutManager = LinearLayoutManager(requireContext()) + adapter = frpcRecyclerAdapter + } + val frpcMoveCallback = ItemMoveCallback(object : ItemMoveCallback.Listener { + override fun onItemMove(fromPosition: Int, toPosition: Int) { + Log.d(TAG, "onItemMove: $fromPosition $toPosition") + frpcRecyclerAdapter.onItemMove(fromPosition, toPosition) + frpcListSelected = frpcRecyclerAdapter.itemList + } + + override fun onDragFinished() { + frpcListSelected = frpcRecyclerAdapter.itemList + //frpcRecyclerAdapter.notifyDataSetChanged() + Log.d(TAG, "onDragFinished: $frpcListSelected") + } + }) + val frpcTouchHelper = ItemTouchHelper(frpcMoveCallback) + frpcTouchHelper.attachToRecyclerView(frpcRecyclerView) + frpcRecyclerAdapter.setTouchHelper(frpcTouchHelper) + + //获取Frpc列表 + getFrpcList() } - /** - * 动态增删Frpc - * - * @param frpcItemMap 管理item的map,用于删除指定header - * @param layoutFrpcs 需要挂载item的LinearLayout - * @param frpc FrpcAdapterItem - */ - @SuppressLint("SetTextI18n") - private fun addFrpcItemLinearLayout( - frpcItemMap: MutableMap, layoutFrpcs: LinearLayout, frpc: FrpcAdapterItem - ) { - val layoutFrpcItem = View.inflate(requireContext(), R.layout.item_add_frpc, null) as LinearLayout - val ivRemoveFrpc = layoutFrpcItem.findViewById(R.id.iv_remove_frpc) - val ivFrpcImage = layoutFrpcItem.findViewById(R.id.iv_frpc_image) - val ivFrpcStatus = layoutFrpcItem.findViewById(R.id.iv_frpc_status) - val tvFrpcName = layoutFrpcItem.findViewById(R.id.tv_frpc_name) + //获取Frpc列表 + private fun getFrpcList() { + Core.frpc.getAll().subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(object : SingleObserver> { + override fun onSubscribe(d: Disposable) {} - ivFrpcImage.setImageDrawable(frpc.icon) - ivFrpcStatus.setImageDrawable(getDrawable(if (STATUS_ON == frpc.autorun) R.drawable.ic_autorun else R.drawable.ic_manual)) - val frpcItemId = frpc.uid - tvFrpcName.text = frpc.title - - ivRemoveFrpc.tag = frpcItemId - ivRemoveFrpc.setOnClickListener { view2: View -> - val tagId = view2.tag - layoutFrpcs.removeView(frpcItemMap[tagId]) - frpcItemMap.remove(tagId) - //frpcListSelected.removeIf { it.uid == tagId } - for (it in frpcListSelected) { - if (it.uid == tagId) { - frpcListSelected -= it - break - } + override fun onError(e: Throwable) { + e.printStackTrace() + Log.e(TAG, "getFrpcList error: ${e.message}") } - Log.d(TAG, frpcListSelected.count().toString()) - Log.d(TAG, frpcListSelected.toString()) - if (frpcListSelected.isEmpty()) frpcUid = "" - } - layoutFrpcs.addView(layoutFrpcItem) - frpcItemMap[frpcItemId] = layoutFrpcItem + + @SuppressLint("NotifyDataSetChanged") + override fun onSuccess(frpcList: List) { + if (frpcList.isEmpty()) { + XToastUtils.error(R.string.add_frpc_first) + return + } + + frpcSpinnerList.clear() + frpcListAll = frpcList as MutableList + for (frpc in frpcList) { + val name = if (frpc.name.length > 20) frpc.name.substring(0, 19) else frpc.name + frpcSpinnerList.add(FrpcSpinnerItem(name, getDrawable(R.drawable.auto_task_icon_frpc), frpc.uid, frpc.autorun)) + } + frpcSpinnerAdapter = FrpcSpinnerAdapter(frpcSpinnerList).setIsFilterKey(true).setFilterColor("#EF5362").setBackgroundSelector(R.drawable.selector_custom_spinner_bg) + binding!!.spFrpc.setAdapter(frpcSpinnerAdapter) + //frpcSpinnerAdapter.notifyDataSetChanged() + + //更新frpcListSelected的状态与名称 + frpcListSelected.forEach { + frpcListAll.forEach { frpc -> + if (it.uid == frpc.uid) { + it.name = frpc.name + it.autorun = frpc.autorun + } + } + } + frpcRecyclerAdapter.notifyDataSetChanged() + + } + }) } //检查设置 diff --git a/app/src/main/java/com/idormy/sms/forwarder/fragment/action/NotificationFragment.kt b/app/src/main/java/com/idormy/sms/forwarder/fragment/action/NotificationFragment.kt index a3cae857..5a1491b2 100644 --- a/app/src/main/java/com/idormy/sms/forwarder/fragment/action/NotificationFragment.kt +++ b/app/src/main/java/com/idormy/sms/forwarder/fragment/action/NotificationFragment.kt @@ -6,10 +6,15 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.* +import androidx.recyclerview.widget.ItemTouchHelper +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView import com.google.gson.Gson import com.idormy.sms.forwarder.R -import com.idormy.sms.forwarder.adapter.spinner.SenderAdapterItem +import com.idormy.sms.forwarder.adapter.SenderRecyclerAdapter +import com.idormy.sms.forwarder.adapter.base.ItemMoveCallback import com.idormy.sms.forwarder.adapter.spinner.SenderSpinnerAdapter +import com.idormy.sms.forwarder.adapter.spinner.SenderSpinnerItem import com.idormy.sms.forwarder.core.BaseFragment import com.idormy.sms.forwarder.core.Core import com.idormy.sms.forwarder.database.entity.Rule @@ -52,16 +57,17 @@ class NotificationFragment : BaseFragment = mutableListOf() - private var senderItemMap = HashMap(2) - - //发送通道列表 - var senderListAll: MutableList = mutableListOf() - private val senderSpinnerList = ArrayList() + //所有发送通道下拉框 + private var senderListAll = mutableListOf() + private val senderSpinnerList = mutableListOf() private lateinit var senderSpinnerAdapter: SenderSpinnerAdapter<*> + //已选发送通道列表 + private var senderId = 0L + private var senderListSelected = mutableListOf() + private lateinit var sendersRecyclerView: RecyclerView + private lateinit var senderRecyclerAdapter: SenderRecyclerAdapter + private var ruleId: Long = 0 private var ruleType: String = "app" @@ -109,6 +115,7 @@ class NotificationFragment : BaseFragment> { - override fun onSubscribe(d: Disposable) {} - - override fun onError(e: Throwable) { - e.printStackTrace() - Log.e(TAG, "initSenderSpinner error: ${e.message}") - } - - override fun onSuccess(senderList: List) { - if (senderList.isEmpty()) { - XToastUtils.error(R.string.add_sender_first) - return - } - - senderListAll = senderList as MutableList - for (sender in senderList) { - val name = if (sender.name.length > 20) sender.name.substring(0, 19) else sender.name - senderSpinnerList.add(SenderAdapterItem(name, getDrawable(sender.imageId), sender.id, sender.status)) - } - senderSpinnerAdapter = SenderSpinnerAdapter(senderSpinnerList) - .setIsFilterKey(true).setFilterColor("#EF5362").setBackgroundSelector(R.drawable.selector_custom_spinner_bg) - binding!!.spSender.setAdapter(senderSpinnerAdapter) - - if (senderListSelected.isNotEmpty()) { - for (sender in senderListSelected) { - for (senderItem in senderSpinnerList) { - if (sender.id == senderItem.id) { - addSenderItemLinearLayout(senderItemMap, binding!!.layoutSenders, senderItem) - } - } - } - } - } - }) + //初始化发送通道下拉框 binding!!.spSender.setOnItemClickListener { _: AdapterView<*>, _: View, position: Int, _: Long -> try { - val sender = senderSpinnerAdapter.getItemSource(position) as SenderAdapterItem - senderId = sender.id!! + val item = senderSpinnerAdapter.getItemSource(position) as SenderSpinnerItem + senderId = item.id!! if (senderId > 0L) { senderListSelected.forEach { if (senderId == it.id) { @@ -346,11 +320,14 @@ class NotificationFragment : BaseFragment + senderListSelected.removeAt(position) + senderRecyclerAdapter.notifyItemRemoved(position) + senderRecyclerAdapter.notifyItemRangeChanged(position, senderListSelected.size) // 更新索引 + checkSenderLogicShow() + }) + sendersRecyclerView.apply { + layoutManager = LinearLayoutManager(requireContext()) + adapter = senderRecyclerAdapter + } + val senderMoveCallback = ItemMoveCallback(object : ItemMoveCallback.Listener { + override fun onItemMove(fromPosition: Int, toPosition: Int) { + Log.d(TAG, "onItemMove: $fromPosition $toPosition") + senderRecyclerAdapter.onItemMove(fromPosition, toPosition) + senderListSelected = senderRecyclerAdapter.itemList + } + + override fun onDragFinished() { + senderListSelected = senderRecyclerAdapter.itemList + //senderRecyclerAdapter.notifyDataSetChanged() + Log.d(TAG, "onDragFinished: $senderListSelected") + } + }) + val senderTouchHelper = ItemTouchHelper(senderMoveCallback) + senderTouchHelper.attachToRecyclerView(sendersRecyclerView) + senderRecyclerAdapter.setTouchHelper(senderTouchHelper) + + //获取发送通道列表 + getSenderList() } - /** - * 动态增删Sender - * - * @param senderItemMap 管理item的map,用于删除指定header - * @param layoutSenders 需要挂载item的LinearLayout - * @param sender SenderAdapterItem - */ - @SuppressLint("SetTextI18n") - private fun addSenderItemLinearLayout( - senderItemMap: MutableMap, layoutSenders: LinearLayout, sender: SenderAdapterItem - ) { - val layoutSenderItem = View.inflate(requireContext(), R.layout.item_add_sender, null) as LinearLayout - val ivRemoveSender = layoutSenderItem.findViewById(R.id.iv_remove_sender) - val ivSenderImage = layoutSenderItem.findViewById(R.id.iv_sender_image) - val ivSenderStatus = layoutSenderItem.findViewById(R.id.iv_sender_status) - val tvSenderName = layoutSenderItem.findViewById(R.id.tv_sender_name) + //获取发送通道列表 + private fun getSenderList() { + Core.sender.getAll().subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(object : SingleObserver> { + override fun onSubscribe(d: Disposable) {} - ivSenderImage.setImageDrawable(sender.icon) - ivSenderStatus.setImageDrawable(getDrawable(if (STATUS_OFF == sender.status) R.drawable.ic_stop else R.drawable.ic_start)) - val senderItemId = sender.id as Long - tvSenderName.text = "ID-$senderItemId:${sender.title}" + override fun onError(e: Throwable) { + e.printStackTrace() + Log.e(TAG, "getSenderList error: ${e.message}") + } - ivRemoveSender.tag = senderItemId - ivRemoveSender.setOnClickListener { view2: View -> - val tagId = view2.tag as Long - layoutSenders.removeView(senderItemMap[tagId]) - senderItemMap.remove(tagId) - //senderListSelected.removeIf { it.id == tagId } - for (it in senderListSelected) { - if (it.id == tagId) { - senderListSelected -= it - break + @SuppressLint("NotifyDataSetChanged") + override fun onSuccess(senderList: List) { + if (senderList.isEmpty()) { + XToastUtils.error(R.string.add_sender_first) + return } + + senderSpinnerList.clear() + senderListAll = senderList as MutableList + for (sender in senderList) { + val name = if (sender.name.length > 20) sender.name.substring(0, 19) else sender.name + senderSpinnerList.add(SenderSpinnerItem(name, getDrawable(sender.imageId), sender.id, sender.status)) + } + senderSpinnerAdapter = SenderSpinnerAdapter(senderSpinnerList).setIsFilterKey(true).setFilterColor("#EF5362").setBackgroundSelector(R.drawable.selector_custom_spinner_bg) + binding!!.spSender.setAdapter(senderSpinnerAdapter) + //senderSpinnerAdapter.notifyDataSetChanged() + + //更新senderListSelected的状态与名称 + senderListSelected.forEach { + senderListAll.forEach { sender -> + if (it.id == sender.id) { + it.name = sender.name + it.status = sender.status + } + } + } + senderRecyclerAdapter.notifyDataSetChanged() + } - Log.d(TAG, senderListSelected.count().toString()) - Log.d(TAG, senderListSelected.toString()) - if (senderListSelected.isEmpty()) senderId = 0L - if (senderListSelected.count() > 1) { - binding!!.layoutSenderLogic.visibility = View.VISIBLE - } else { - binding!!.layoutSenderLogic.visibility = View.GONE - binding!!.rgSenderLogic.check(R.id.rb_sender_logic_all) - } - } - layoutSenders.addView(layoutSenderItem) - senderItemMap[senderItemId] = layoutSenderItem - if (senderListSelected.count() > 1) { + }) + } + + private fun checkSenderLogicShow() { + if (senderListSelected.size > 1) { binding!!.layoutSenderLogic.visibility = View.VISIBLE } else { binding!!.layoutSenderLogic.visibility = View.GONE diff --git a/app/src/main/java/com/idormy/sms/forwarder/fragment/action/RuleFragment.kt b/app/src/main/java/com/idormy/sms/forwarder/fragment/action/RuleFragment.kt index 84b7b384..2816e9b4 100644 --- a/app/src/main/java/com/idormy/sms/forwarder/fragment/action/RuleFragment.kt +++ b/app/src/main/java/com/idormy/sms/forwarder/fragment/action/RuleFragment.kt @@ -6,27 +6,33 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.AdapterView -import android.widget.ImageView -import android.widget.LinearLayout -import android.widget.TextView +import androidx.recyclerview.widget.ItemTouchHelper +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView +import androidx.work.Data +import androidx.work.OneTimeWorkRequestBuilder +import androidx.work.WorkManager import com.google.gson.Gson import com.idormy.sms.forwarder.R -import com.idormy.sms.forwarder.adapter.spinner.RuleAdapterItem +import com.idormy.sms.forwarder.adapter.RuleRecyclerAdapter +import com.idormy.sms.forwarder.adapter.base.ItemMoveCallback import com.idormy.sms.forwarder.adapter.spinner.RuleSpinnerAdapter +import com.idormy.sms.forwarder.adapter.spinner.RuleSpinnerItem import com.idormy.sms.forwarder.core.BaseFragment import com.idormy.sms.forwarder.core.Core import com.idormy.sms.forwarder.database.entity.Rule import com.idormy.sms.forwarder.databinding.FragmentTasksActionRuleBinding +import com.idormy.sms.forwarder.entity.MsgInfo +import com.idormy.sms.forwarder.entity.TaskSetting import com.idormy.sms.forwarder.entity.action.RuleSetting import com.idormy.sms.forwarder.utils.KEY_BACK_DATA_ACTION import com.idormy.sms.forwarder.utils.KEY_BACK_DESCRIPTION_ACTION import com.idormy.sms.forwarder.utils.KEY_EVENT_DATA_ACTION -import com.idormy.sms.forwarder.utils.KEY_TEST_ACTION import com.idormy.sms.forwarder.utils.Log -import com.idormy.sms.forwarder.utils.STATUS_OFF import com.idormy.sms.forwarder.utils.TASK_ACTION_RULE +import com.idormy.sms.forwarder.utils.TaskWorker import com.idormy.sms.forwarder.utils.XToastUtils -import com.jeremyliao.liveeventbus.LiveEventBus +import com.idormy.sms.forwarder.workers.ActionWorker import com.xuexiang.xaop.annotation.SingleClick import com.xuexiang.xpage.annotation.Page import com.xuexiang.xrouter.annotation.AutoWired @@ -38,25 +44,27 @@ import io.reactivex.SingleObserver import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.disposables.Disposable import io.reactivex.schedulers.Schedulers +import java.util.Date @Page(name = "Rule") -@Suppress("PrivatePropertyName") +@Suppress("PrivatePropertyName", "DEPRECATION") class RuleFragment : BaseFragment(), View.OnClickListener { private val TAG: String = RuleFragment::class.java.simpleName private var titleBar: TitleBar? = null private var mCountDownHelper: CountDownButtonHelper? = null - //当前转发规则 - private var ruleId = 0L - private var ruleListSelected: MutableList = mutableListOf() - private var ruleItemMap = HashMap(2) - - //转发规则列表 - private var ruleListAll: MutableList = mutableListOf() - private val ruleSpinnerList = ArrayList() + //所有转发规则下拉框 + private var ruleListAll = mutableListOf() + private val ruleSpinnerList = mutableListOf() private lateinit var ruleSpinnerAdapter: RuleSpinnerAdapter<*> + //已选转发规则列表 + private var ruleId = 0L + private var ruleListSelected = mutableListOf() + private lateinit var ruleRecyclerView: RecyclerView + private lateinit var ruleRecyclerAdapter: RuleRecyclerAdapter + @JvmField @AutoWired(name = KEY_EVENT_DATA_ACTION) var eventData: String? = null @@ -82,7 +90,7 @@ class RuleFragment : BaseFragment(), View.OnCli */ override fun initViews() { //测试按钮增加倒计时,避免重复点击 - mCountDownHelper = CountDownButtonHelper(binding!!.btnTest, 3) + mCountDownHelper = CountDownButtonHelper(binding!!.btnTest, 1) mCountDownHelper!!.setOnCountDownListener(object : CountDownButtonHelper.OnCountDownListener { override fun onCountDown(time: Int) { binding!!.btnTest.text = String.format(getString(R.string.seconds_n), time) @@ -90,6 +98,8 @@ class RuleFragment : BaseFragment(), View.OnCli override fun onFinished() { binding!!.btnTest.text = getString(R.string.test) + //获取转发规则列表 + getRuleList() } }) @@ -106,7 +116,7 @@ class RuleFragment : BaseFragment(), View.OnCli } //初始化转发规则下拉框 - initRuleSpinner() + initRule() } @SuppressLint("SetTextI18n") @@ -114,15 +124,6 @@ class RuleFragment : BaseFragment(), View.OnCli binding!!.btnTest.setOnClickListener(this) binding!!.btnDel.setOnClickListener(this) binding!!.btnSave.setOnClickListener(this) - LiveEventBus.get(KEY_TEST_ACTION, String::class.java).observe(this) { - mCountDownHelper?.finish() - - if (it == "success") { - XToastUtils.success("测试通过", 30000) - } else { - XToastUtils.error(it, 30000) - } - } } @SingleClick @@ -131,17 +132,21 @@ class RuleFragment : BaseFragment(), View.OnCli when (v.id) { R.id.btn_test -> { mCountDownHelper?.start() - Thread { - try { - val settingVo = checkSetting() - Log.d(TAG, settingVo.toString()) - LiveEventBus.get(KEY_TEST_ACTION, String::class.java).post("success") - } catch (e: Exception) { - LiveEventBus.get(KEY_TEST_ACTION, String::class.java).post(e.message.toString()) - e.printStackTrace() - Log.e(TAG, "onClick error: ${e.message}") - } - }.start() + try { + val settingVo = checkSetting() + Log.d(TAG, settingVo.toString()) + val taskAction = TaskSetting(TASK_ACTION_RULE, getString(R.string.task_rule), settingVo.description, Gson().toJson(settingVo), requestCode) + val taskActionsJson = Gson().toJson(arrayListOf(taskAction)) + val msgInfo = MsgInfo("task", getString(R.string.task_rule), settingVo.description, Date(), getString(R.string.task_rule)) + val actionData = Data.Builder().putLong(TaskWorker.taskId, 0).putString(TaskWorker.taskActions, taskActionsJson).putString(TaskWorker.msgInfo, Gson().toJson(msgInfo)).build() + val actionRequest = OneTimeWorkRequestBuilder().setInputData(actionData).build() + WorkManager.getInstance().enqueue(actionRequest) + } catch (e: Exception) { + mCountDownHelper?.finish() + e.printStackTrace() + Log.e(TAG, "onClick error: ${e.message}") + XToastUtils.error(e.message.toString(), 30000) + } return } @@ -167,53 +172,14 @@ class RuleFragment : BaseFragment(), View.OnCli } } - //初始化转发规则下拉框 - @SuppressLint("SetTextI18n") - private fun initRuleSpinner() { - Core.rule.getAll().subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(object : SingleObserver> { - override fun onSubscribe(d: Disposable) {} - - override fun onError(e: Throwable) { - e.printStackTrace() - Log.e(TAG, "initRuleSpinner error: ${e.message}") - } - - override fun onSuccess(ruleList: List) { - if (ruleList.isEmpty()) { - XToastUtils.error(R.string.add_rule_first) - return - } - - ruleListAll = ruleList as MutableList - for (rule in ruleList) { - val name = if (rule.name.length > 20) rule.name.substring(0, 19) else rule.name - val icon = when (rule.type) { - "sms" -> R.drawable.auto_task_icon_sms - "call" -> R.drawable.auto_task_icon_incall - "app" -> R.drawable.auto_task_icon_start_activity - else -> R.drawable.auto_task_icon_sms - } - ruleSpinnerList.add(RuleAdapterItem(name, getDrawable(icon), rule.id, rule.status)) - } - ruleSpinnerAdapter = RuleSpinnerAdapter(ruleSpinnerList) - .setIsFilterKey(true).setFilterColor("#EF5362").setBackgroundSelector(R.drawable.selector_custom_spinner_bg) - binding!!.spRule.setAdapter(ruleSpinnerAdapter) - - if (ruleListSelected.isNotEmpty()) { - for (rule in ruleListSelected) { - for (ruleItem in ruleSpinnerList) { - if (rule.id == ruleItem.id) { - addRuleItemLinearLayout(ruleItemMap, binding!!.layoutRules, ruleItem) - } - } - } - } - } - }) + //初始化转发规则 + @SuppressLint("SetTextI18n", "NotifyDataSetChanged") + private fun initRule() { + //初始化转发规则下拉框 binding!!.spRule.setOnItemClickListener { _: AdapterView<*>, _: View, position: Int, _: Long -> try { - val rule = ruleSpinnerAdapter.getItemSource(position) as RuleAdapterItem - ruleId = rule.id!! + val item = ruleSpinnerAdapter.getItemSource(position) as RuleSpinnerItem + ruleId = item.id!! if (ruleId > 0L) { ruleListSelected.forEach { if (ruleId == it.id) { @@ -224,67 +190,100 @@ class RuleFragment : BaseFragment(), View.OnCli ruleListAll.forEach { if (ruleId == it.id) { ruleListSelected.add(it) - addRuleItemLinearLayout(ruleItemMap, binding!!.layoutRules, rule) } } - - /*if (STATUS_OFF == rule.status) { - XToastUtils.warning(getString(R.string.rule_disabled_tips)) - }*/ + ruleRecyclerAdapter.notifyDataSetChanged() } } catch (e: Exception) { XToastUtils.error(e.message.toString()) } } + + // 初始化已选转发规则列表 RecyclerView 和 Adapter + ruleRecyclerView = binding!!.recyclerRules + ruleRecyclerAdapter = RuleRecyclerAdapter(ruleListSelected, { position -> + ruleListSelected.removeAt(position) + ruleRecyclerAdapter.notifyItemRemoved(position) + ruleRecyclerAdapter.notifyItemRangeChanged(position, ruleListSelected.size) // 更新索引 + }) + ruleRecyclerView.apply { + layoutManager = LinearLayoutManager(requireContext()) + adapter = ruleRecyclerAdapter + } + val ruleMoveCallback = ItemMoveCallback(object : ItemMoveCallback.Listener { + override fun onItemMove(fromPosition: Int, toPosition: Int) { + Log.d(TAG, "onItemMove: $fromPosition $toPosition") + ruleRecyclerAdapter.onItemMove(fromPosition, toPosition) + ruleListSelected = ruleRecyclerAdapter.itemList + } + + override fun onDragFinished() { + ruleListSelected = ruleRecyclerAdapter.itemList + //ruleRecyclerAdapter.notifyDataSetChanged() + Log.d(TAG, "onDragFinished: $ruleListSelected") + } + }) + val ruleTouchHelper = ItemTouchHelper(ruleMoveCallback) + ruleTouchHelper.attachToRecyclerView(ruleRecyclerView) + ruleRecyclerAdapter.setTouchHelper(ruleTouchHelper) + + //获取转发规则列表 + getRuleList() } - /** - * 动态增删Rule - * - * @param ruleItemMap 管理item的map,用于删除指定header - * @param layoutRules 需要挂载item的LinearLayout - * @param rule RuleAdapterItem - */ - @SuppressLint("SetTextI18n") - private fun addRuleItemLinearLayout( - ruleItemMap: MutableMap, layoutRules: LinearLayout, rule: RuleAdapterItem - ) { - val layoutRuleItem = View.inflate(requireContext(), R.layout.item_add_rule, null) as LinearLayout - val ivRemoveRule = layoutRuleItem.findViewById(R.id.iv_remove_rule) - val ivRuleImage = layoutRuleItem.findViewById(R.id.iv_rule_image) - val ivRuleStatus = layoutRuleItem.findViewById(R.id.iv_rule_status) - val tvRuleName = layoutRuleItem.findViewById(R.id.tv_rule_name) + //获取转发规则列表 + private fun getRuleList() { + Core.rule.getAll().subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(object : SingleObserver> { + override fun onSubscribe(d: Disposable) {} - ivRuleImage.setImageDrawable(rule.icon) - ivRuleStatus.setImageDrawable(getDrawable(if (STATUS_OFF == rule.status) R.drawable.ic_stop else R.drawable.ic_start)) - val ruleItemId = rule.id as Long - tvRuleName.text = "ID-$ruleItemId:${rule.title}" - - ivRemoveRule.tag = ruleItemId - ivRemoveRule.setOnClickListener { view2: View -> - val tagId = view2.tag as Long - layoutRules.removeView(ruleItemMap[tagId]) - ruleItemMap.remove(tagId) - //ruleListSelected.removeIf { it.id == tagId } - for (it in ruleListSelected) { - if (it.id == tagId) { - ruleListSelected -= it - break - } + override fun onError(e: Throwable) { + e.printStackTrace() + Log.e(TAG, "getRuleList error: ${e.message}") } - Log.d(TAG, ruleListSelected.count().toString()) - Log.d(TAG, ruleListSelected.toString()) - if (ruleListSelected.isEmpty()) ruleId = 0L - } - layoutRules.addView(layoutRuleItem) - ruleItemMap[ruleItemId] = layoutRuleItem + + @SuppressLint("NotifyDataSetChanged") + override fun onSuccess(ruleList: List) { + if (ruleList.isEmpty()) { + XToastUtils.error(R.string.add_rule_first) + return + } + + ruleSpinnerList.clear() + ruleListAll = ruleList as MutableList + for (rule in ruleList) { + val name = if (rule.name.length > 20) rule.name.substring(0, 19) else rule.name + val icon = when (rule.type) { + "sms" -> R.drawable.auto_task_icon_sms + "call" -> R.drawable.auto_task_icon_incall + "app" -> R.drawable.auto_task_icon_start_activity + else -> R.drawable.auto_task_icon_sms + } + ruleSpinnerList.add(RuleSpinnerItem(name, getDrawable(icon), rule.id, rule.status)) + } + ruleSpinnerAdapter = RuleSpinnerAdapter(ruleSpinnerList).setIsFilterKey(true).setFilterColor("#EF5362").setBackgroundSelector(R.drawable.selector_custom_spinner_bg) + binding!!.spRule.setAdapter(ruleSpinnerAdapter) + //ruleSpinnerAdapter.notifyDataSetChanged() + + //更新ruleListSelected的状态与名称 + ruleListSelected.forEach { + ruleListAll.forEach { rule -> + if (it.id == rule.id) { + //it.name = rule.name + it.status = rule.status + } + } + } + ruleRecyclerAdapter.notifyDataSetChanged() + + } + }) } //检查设置 @SuppressLint("SetTextI18n") private fun checkSetting(): RuleSetting { if (ruleListSelected.isEmpty() || ruleId == 0L) { - throw Exception(getString(R.string.new_sender_first)) + throw Exception(getString(R.string.new_rule_first)) } val description = StringBuilder() diff --git a/app/src/main/java/com/idormy/sms/forwarder/fragment/action/SenderFragment.kt b/app/src/main/java/com/idormy/sms/forwarder/fragment/action/SenderFragment.kt index 0366f6aa..78bb3bca 100644 --- a/app/src/main/java/com/idormy/sms/forwarder/fragment/action/SenderFragment.kt +++ b/app/src/main/java/com/idormy/sms/forwarder/fragment/action/SenderFragment.kt @@ -6,27 +6,33 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.AdapterView -import android.widget.ImageView -import android.widget.LinearLayout -import android.widget.TextView +import androidx.recyclerview.widget.ItemTouchHelper +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView +import androidx.work.Data +import androidx.work.OneTimeWorkRequestBuilder +import androidx.work.WorkManager import com.google.gson.Gson import com.idormy.sms.forwarder.R -import com.idormy.sms.forwarder.adapter.spinner.SenderAdapterItem +import com.idormy.sms.forwarder.adapter.SenderRecyclerAdapter +import com.idormy.sms.forwarder.adapter.base.ItemMoveCallback import com.idormy.sms.forwarder.adapter.spinner.SenderSpinnerAdapter +import com.idormy.sms.forwarder.adapter.spinner.SenderSpinnerItem import com.idormy.sms.forwarder.core.BaseFragment import com.idormy.sms.forwarder.core.Core import com.idormy.sms.forwarder.database.entity.Sender import com.idormy.sms.forwarder.databinding.FragmentTasksActionSenderBinding +import com.idormy.sms.forwarder.entity.MsgInfo +import com.idormy.sms.forwarder.entity.TaskSetting import com.idormy.sms.forwarder.entity.action.SenderSetting import com.idormy.sms.forwarder.utils.KEY_BACK_DATA_ACTION import com.idormy.sms.forwarder.utils.KEY_BACK_DESCRIPTION_ACTION import com.idormy.sms.forwarder.utils.KEY_EVENT_DATA_ACTION -import com.idormy.sms.forwarder.utils.KEY_TEST_ACTION import com.idormy.sms.forwarder.utils.Log -import com.idormy.sms.forwarder.utils.STATUS_OFF import com.idormy.sms.forwarder.utils.TASK_ACTION_SENDER +import com.idormy.sms.forwarder.utils.TaskWorker import com.idormy.sms.forwarder.utils.XToastUtils -import com.jeremyliao.liveeventbus.LiveEventBus +import com.idormy.sms.forwarder.workers.ActionWorker import com.xuexiang.xaop.annotation.SingleClick import com.xuexiang.xpage.annotation.Page import com.xuexiang.xrouter.annotation.AutoWired @@ -38,25 +44,27 @@ import io.reactivex.SingleObserver import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.disposables.Disposable import io.reactivex.schedulers.Schedulers +import java.util.Date @Page(name = "Sender") -@Suppress("PrivatePropertyName") +@Suppress("PrivatePropertyName", "DEPRECATION") class SenderFragment : BaseFragment(), View.OnClickListener { private val TAG: String = SenderFragment::class.java.simpleName private var titleBar: TitleBar? = null private var mCountDownHelper: CountDownButtonHelper? = null - //当前发送通道 - private var senderId = 0L - private var senderListSelected: MutableList = mutableListOf() - private var senderItemMap = HashMap(2) - - //发送通道列表 - private var senderListAll: MutableList = mutableListOf() - private val senderSpinnerList = ArrayList() + //所有发送通道下拉框 + private var senderListAll = mutableListOf() + private val senderSpinnerList = mutableListOf() private lateinit var senderSpinnerAdapter: SenderSpinnerAdapter<*> + //已选发送通道列表 + private var senderId = 0L + private var senderListSelected = mutableListOf() + private lateinit var senderRecyclerView: RecyclerView + private lateinit var senderRecyclerAdapter: SenderRecyclerAdapter + @JvmField @AutoWired(name = KEY_EVENT_DATA_ACTION) var eventData: String? = null @@ -80,9 +88,10 @@ class SenderFragment : BaseFragment(), View.O /** * 初始化控件 */ + @SuppressLint("NotifyDataSetChanged") override fun initViews() { //测试按钮增加倒计时,避免重复点击 - mCountDownHelper = CountDownButtonHelper(binding!!.btnTest, 3) + mCountDownHelper = CountDownButtonHelper(binding!!.btnTest, 1) mCountDownHelper!!.setOnCountDownListener(object : CountDownButtonHelper.OnCountDownListener { override fun onCountDown(time: Int) { binding!!.btnTest.text = String.format(getString(R.string.seconds_n), time) @@ -90,6 +99,8 @@ class SenderFragment : BaseFragment(), View.O override fun onFinished() { binding!!.btnTest.text = getString(R.string.test) + //获取发送通道列表 + getSenderList() } }) @@ -106,7 +117,7 @@ class SenderFragment : BaseFragment(), View.O } //初始化发送通道下拉框 - initSenderSpinner() + initSender() } @SuppressLint("SetTextI18n") @@ -114,15 +125,6 @@ class SenderFragment : BaseFragment(), View.O binding!!.btnTest.setOnClickListener(this) binding!!.btnDel.setOnClickListener(this) binding!!.btnSave.setOnClickListener(this) - LiveEventBus.get(KEY_TEST_ACTION, String::class.java).observe(this) { - mCountDownHelper?.finish() - - if (it == "success") { - XToastUtils.success("测试通过", 30000) - } else { - XToastUtils.error(it, 30000) - } - } } @SingleClick @@ -131,17 +133,21 @@ class SenderFragment : BaseFragment(), View.O when (v.id) { R.id.btn_test -> { mCountDownHelper?.start() - Thread { - try { - val settingVo = checkSetting() - Log.d(TAG, settingVo.toString()) - LiveEventBus.get(KEY_TEST_ACTION, String::class.java).post("success") - } catch (e: Exception) { - LiveEventBus.get(KEY_TEST_ACTION, String::class.java).post(e.message.toString()) - e.printStackTrace() - Log.e(TAG, "onClick error: ${e.message}") - } - }.start() + try { + val settingVo = checkSetting() + Log.d(TAG, settingVo.toString()) + val taskAction = TaskSetting(TASK_ACTION_SENDER, getString(R.string.task_sender), settingVo.description, Gson().toJson(settingVo), requestCode) + val taskActionsJson = Gson().toJson(arrayListOf(taskAction)) + val msgInfo = MsgInfo("task", getString(R.string.task_sender), settingVo.description, Date(), getString(R.string.task_sender)) + val actionData = Data.Builder().putLong(TaskWorker.taskId, 0).putString(TaskWorker.taskActions, taskActionsJson).putString(TaskWorker.msgInfo, Gson().toJson(msgInfo)).build() + val actionRequest = OneTimeWorkRequestBuilder().setInputData(actionData).build() + WorkManager.getInstance().enqueue(actionRequest) + } catch (e: Exception) { + mCountDownHelper?.finish() + e.printStackTrace() + Log.e(TAG, "onClick error: ${e.message}") + XToastUtils.error(e.message.toString(), 30000) + } return } @@ -167,47 +173,14 @@ class SenderFragment : BaseFragment(), View.O } } - //初始化发送通道下拉框 - @SuppressLint("SetTextI18n") - private fun initSenderSpinner() { - Core.sender.getAll().subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(object : SingleObserver> { - override fun onSubscribe(d: Disposable) {} - - override fun onError(e: Throwable) { - e.printStackTrace() - Log.e(TAG, "initSenderSpinner error: ${e.message}") - } - - override fun onSuccess(senderList: List) { - if (senderList.isEmpty()) { - XToastUtils.error(R.string.add_sender_first) - return - } - - senderListAll = senderList as MutableList - for (sender in senderList) { - val name = if (sender.name.length > 20) sender.name.substring(0, 19) else sender.name - senderSpinnerList.add(SenderAdapterItem(name, getDrawable(sender.imageId), sender.id, sender.status)) - } - senderSpinnerAdapter = SenderSpinnerAdapter(senderSpinnerList) - .setIsFilterKey(true).setFilterColor("#EF5362").setBackgroundSelector(R.drawable.selector_custom_spinner_bg) - binding!!.spSender.setAdapter(senderSpinnerAdapter) - - if (senderListSelected.isNotEmpty()) { - for (sender in senderListSelected) { - for (senderItem in senderSpinnerList) { - if (sender.id == senderItem.id) { - addSenderItemLinearLayout(senderItemMap, binding!!.layoutSenders, senderItem) - } - } - } - } - } - }) + //初始化发送通道 + @SuppressLint("SetTextI18n", "NotifyDataSetChanged") + private fun initSender() { + //初始化发送通道下拉框 binding!!.spSender.setOnItemClickListener { _: AdapterView<*>, _: View, position: Int, _: Long -> try { - val sender = senderSpinnerAdapter.getItemSource(position) as SenderAdapterItem - senderId = sender.id!! + val item = senderSpinnerAdapter.getItemSource(position) as SenderSpinnerItem + senderId = item.id!! if (senderId > 0L) { senderListSelected.forEach { if (senderId == it.id) { @@ -218,60 +191,87 @@ class SenderFragment : BaseFragment(), View.O senderListAll.forEach { if (senderId == it.id) { senderListSelected.add(it) - addSenderItemLinearLayout(senderItemMap, binding!!.layoutSenders, sender) } } - - /*if (STATUS_OFF == sender.status) { - XToastUtils.warning(getString(R.string.sender_disabled_tips)) - }*/ + senderRecyclerAdapter.notifyDataSetChanged() } } catch (e: Exception) { XToastUtils.error(e.message.toString()) } } + + // 初始化已选发送通道列表 RecyclerView 和 Adapter + senderRecyclerView = binding!!.recyclerSenders + senderRecyclerAdapter = SenderRecyclerAdapter(senderListSelected, { position -> + senderListSelected.removeAt(position) + senderRecyclerAdapter.notifyItemRemoved(position) + senderRecyclerAdapter.notifyItemRangeChanged(position, senderListSelected.size) // 更新索引 + }) + senderRecyclerView.apply { + layoutManager = LinearLayoutManager(requireContext()) + adapter = senderRecyclerAdapter + } + val senderMoveCallback = ItemMoveCallback(object : ItemMoveCallback.Listener { + override fun onItemMove(fromPosition: Int, toPosition: Int) { + Log.d(TAG, "onItemMove: $fromPosition $toPosition") + senderRecyclerAdapter.onItemMove(fromPosition, toPosition) + senderListSelected = senderRecyclerAdapter.itemList + } + + override fun onDragFinished() { + senderListSelected = senderRecyclerAdapter.itemList + //senderRecyclerAdapter.notifyDataSetChanged() + Log.d(TAG, "onDragFinished: $senderListSelected") + } + }) + val senderTouchHelper = ItemTouchHelper(senderMoveCallback) + senderTouchHelper.attachToRecyclerView(senderRecyclerView) + senderRecyclerAdapter.setTouchHelper(senderTouchHelper) + + //获取发送通道列表 + getSenderList() } - /** - * 动态增删Sender - * - * @param senderItemMap 管理item的map,用于删除指定header - * @param layoutSenders 需要挂载item的LinearLayout - * @param sender SenderAdapterItem - */ - @SuppressLint("SetTextI18n") - private fun addSenderItemLinearLayout( - senderItemMap: MutableMap, layoutSenders: LinearLayout, sender: SenderAdapterItem - ) { - val layoutSenderItem = View.inflate(requireContext(), R.layout.item_add_sender, null) as LinearLayout - val ivRemoveSender = layoutSenderItem.findViewById(R.id.iv_remove_sender) - val ivSenderImage = layoutSenderItem.findViewById(R.id.iv_sender_image) - val ivSenderStatus = layoutSenderItem.findViewById(R.id.iv_sender_status) - val tvSenderName = layoutSenderItem.findViewById(R.id.tv_sender_name) + //获取发送通道列表 + private fun getSenderList() { + Core.sender.getAll().subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(object : SingleObserver> { + override fun onSubscribe(d: Disposable) {} - ivSenderImage.setImageDrawable(sender.icon) - ivSenderStatus.setImageDrawable(getDrawable(if (STATUS_OFF == sender.status) R.drawable.ic_stop else R.drawable.ic_start)) - val senderItemId = sender.id as Long - tvSenderName.text = "ID-$senderItemId:${sender.title}" - - ivRemoveSender.tag = senderItemId - ivRemoveSender.setOnClickListener { view2: View -> - val tagId = view2.tag as Long - layoutSenders.removeView(senderItemMap[tagId]) - senderItemMap.remove(tagId) - //senderListSelected.removeIf { it.id == tagId } - for (it in senderListSelected) { - if (it.id == tagId) { - senderListSelected -= it - break - } + override fun onError(e: Throwable) { + e.printStackTrace() + Log.e(TAG, "getSenderList error: ${e.message}") } - Log.d(TAG, senderListSelected.count().toString()) - Log.d(TAG, senderListSelected.toString()) - if (senderListSelected.isEmpty()) senderId = 0L - } - layoutSenders.addView(layoutSenderItem) - senderItemMap[senderItemId] = layoutSenderItem + + @SuppressLint("NotifyDataSetChanged") + override fun onSuccess(senderList: List) { + if (senderList.isEmpty()) { + XToastUtils.error(R.string.add_sender_first) + return + } + + senderSpinnerList.clear() + senderListAll = senderList as MutableList + for (sender in senderList) { + val name = if (sender.name.length > 20) sender.name.substring(0, 19) else sender.name + senderSpinnerList.add(SenderSpinnerItem(name, getDrawable(sender.imageId), sender.id, sender.status)) + } + senderSpinnerAdapter = SenderSpinnerAdapter(senderSpinnerList).setIsFilterKey(true).setFilterColor("#EF5362").setBackgroundSelector(R.drawable.selector_custom_spinner_bg) + binding!!.spSender.setAdapter(senderSpinnerAdapter) + //senderSpinnerAdapter.notifyDataSetChanged() + + //更新senderListSelected的状态与名称 + senderListSelected.forEach { + senderListAll.forEach { sender -> + if (it.id == sender.id) { + it.name = sender.name + it.status = sender.status + } + } + } + senderRecyclerAdapter.notifyDataSetChanged() + + } + }) } //检查设置 @@ -295,4 +295,5 @@ class SenderFragment : BaseFragment(), View.O return SenderSetting(description.toString(), status, senderListSelected) } + } \ No newline at end of file diff --git a/app/src/main/java/com/idormy/sms/forwarder/utils/SmsCommandUtils.kt b/app/src/main/java/com/idormy/sms/forwarder/utils/SmsCommandUtils.kt index 3f290666..b2b27cf9 100644 --- a/app/src/main/java/com/idormy/sms/forwarder/utils/SmsCommandUtils.kt +++ b/app/src/main/java/com/idormy/sms/forwarder/utils/SmsCommandUtils.kt @@ -53,7 +53,7 @@ class SmsCommandUtils { Core.frpc.getAutorun() } else { val uids = param.split(",") - Core.frpc.getByUids(uids) + Core.frpc.getByUids(uids, param) } if (frpcList.isEmpty()) { diff --git a/app/src/main/res/layout/adapter_frpc_list_item.xml b/app/src/main/res/layout/adapter_frpc_list_item.xml new file mode 100644 index 00000000..cd17dd39 --- /dev/null +++ b/app/src/main/res/layout/adapter_frpc_list_item.xml @@ -0,0 +1,87 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/adapter_rule_list_item.xml b/app/src/main/res/layout/adapter_rule_list_item.xml new file mode 100644 index 00000000..cd17dd39 --- /dev/null +++ b/app/src/main/res/layout/adapter_rule_list_item.xml @@ -0,0 +1,87 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/adapter_sender_list_item.xml b/app/src/main/res/layout/adapter_sender_list_item.xml new file mode 100644 index 00000000..cd17dd39 --- /dev/null +++ b/app/src/main/res/layout/adapter_sender_list_item.xml @@ -0,0 +1,87 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/adapter_tasks_card_view_list_item.xml b/app/src/main/res/layout/adapter_tasks_card_view_list_item.xml index 60f3e0bb..35adbd7d 100644 --- a/app/src/main/res/layout/adapter_tasks_card_view_list_item.xml +++ b/app/src/main/res/layout/adapter_tasks_card_view_list_item.xml @@ -107,44 +107,22 @@ android:gravity="start" android:maxEms="10" android:maxLines="1" + android:text="@string/task_cron" android:textSize="@dimen/text_size_medium" android:textStyle="bold" /> - - - - - + + android:text="@string/task_cron_tips" + android:textSize="@dimen/text_size_mini" /> - + + + + + diff --git a/app/src/main/res/layout/fragment_rules_edit.xml b/app/src/main/res/layout/fragment_rules_edit.xml index fe485c4a..3adb984f 100644 --- a/app/src/main/res/layout/fragment_rules_edit.xml +++ b/app/src/main/res/layout/fragment_rules_edit.xml @@ -50,11 +50,10 @@ - + android:layout_height="wrap_content" /> + style="@style/BarTitleStyle" + android:layout_marginTop="@dimen/config_margin_5dp"> - + - + - + - + android:layout_marginTop="@dimen/config_margin_5dp" /> diff --git a/app/src/main/res/layout/fragment_tasks_action_notification.xml b/app/src/main/res/layout/fragment_tasks_action_notification.xml index 5387303c..fa67b73c 100644 --- a/app/src/main/res/layout/fragment_tasks_action_notification.xml +++ b/app/src/main/res/layout/fragment_tasks_action_notification.xml @@ -59,11 +59,10 @@ - + android:layout_height="wrap_content" /> - + android:layout_marginTop="@dimen/config_margin_5dp" /> diff --git a/app/src/main/res/layout/fragment_tasks_action_sender.xml b/app/src/main/res/layout/fragment_tasks_action_sender.xml index a5f14fb6..41f1f67e 100644 --- a/app/src/main/res/layout/fragment_tasks_action_sender.xml +++ b/app/src/main/res/layout/fragment_tasks_action_sender.xml @@ -103,12 +103,11 @@ - + android:layout_marginTop="@dimen/config_margin_5dp" /> diff --git a/app/src/main/res/layout/fragment_tasks_action_settings.xml b/app/src/main/res/layout/fragment_tasks_action_settings.xml index 64854414..9c5ef77f 100644 --- a/app/src/main/res/layout/fragment_tasks_action_settings.xml +++ b/app/src/main/res/layout/fragment_tasks_action_settings.xml @@ -28,14 +28,7 @@ app:srcCompat="@drawable/auto_task_icon_settings" tools:ignore="ImageContrastCheck" /> - + - + + android:orientation="horizontal" + android:visibility="gone"> + android:orientation="horizontal" + android:visibility="gone"> - + @@ -137,14 +130,7 @@ - + - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/item_add_rule.xml b/app/src/main/res/layout/item_add_rule.xml deleted file mode 100644 index 061cce30..00000000 --- a/app/src/main/res/layout/item_add_rule.xml +++ /dev/null @@ -1,68 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/item_add_sender.xml b/app/src/main/res/layout/item_add_sender.xml deleted file mode 100644 index 16af3b0f..00000000 --- a/app/src/main/res/layout/item_add_sender.xml +++ /dev/null @@ -1,68 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/values-en/styles_widget.xml b/app/src/main/res/values-en/styles_widget.xml new file mode 100644 index 00000000..9c72f2f2 --- /dev/null +++ b/app/src/main/res/values-en/styles_widget.xml @@ -0,0 +1,200 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index 39930892..10af3bea 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -590,7 +590,7 @@ 超时 单次超时 - %s 秒 + %s秒 重试 最多重试 【%s】恭喜您,该发送通道测试成功,请继续添加转发规则! diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index 9a165a8d..d8f7fd87 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -590,7 +590,7 @@ 超時 單次超時 - %s 秒 + %s秒 重試 最多重試 【%s】恭喜您,該發送通道測試成功,請繼續添加轉發規則! diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 77033306..9065b279 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -590,7 +590,7 @@ 超时 单次超时 - %s 秒 + %s秒 重试 最多重试 【%s】恭喜您,该发送通道测试成功,请继续添加转发规则! diff --git a/app/src/main/res/values/styles_widget.xml b/app/src/main/res/values/styles_widget.xml index 9ccc4f62..9009eb5b 100644 --- a/app/src/main/res/values/styles_widget.xml +++ b/app/src/main/res/values/styles_widget.xml @@ -105,6 +105,20 @@ center_vertical + + + +