新增:自动任务·快捷指令 (开发中)

This commit is contained in:
pppscn 2023-12-26 23:49:19 +08:00
parent e69716affc
commit 0c950c835a
45 changed files with 1572 additions and 1011 deletions

View File

@ -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<Frpc>,
private var removeClickListener: ((Int) -> Unit)? = null,
private var editClickListener: ((Int) -> Unit)? = null,
) : RecyclerView.Adapter<FrpcRecyclerAdapter.ViewHolder>(), 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() {}
}

View File

@ -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<Rule>,
private var removeClickListener: ((Int) -> Unit)? = null,
private var editClickListener: ((Int) -> Unit)? = null,
) : RecyclerView.Adapter<RuleRecyclerAdapter.ViewHolder>(), 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() {}
}

View File

@ -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<Sender>,
private var removeClickListener: ((Int) -> Unit)? = null,
private var editClickListener: ((Int) -> Unit)? = null,
) : RecyclerView.Adapter<SenderRecyclerAdapter.ViewHolder>(), 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() {}
}

View File

@ -10,20 +10,21 @@ import android.widget.TextView
import androidx.recyclerview.widget.ItemTouchHelper import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.idormy.sms.forwarder.R import com.idormy.sms.forwarder.R
import com.idormy.sms.forwarder.adapter.base.ItemMoveCallback
import com.idormy.sms.forwarder.entity.TaskSetting import com.idormy.sms.forwarder.entity.TaskSetting
import java.util.Collections
@Suppress("DEPRECATION") @Suppress("DEPRECATION")
class TaskSettingAdapter( class TaskSettingAdapter(
val itemList: MutableList<TaskSetting>, var itemList: MutableList<TaskSetting>,
private val editClickListener: (Int) -> Unit, private var removeClickListener: ((Int) -> Unit)? = null,
private val removeClickListener: (Int) -> Unit private var editClickListener: ((Int) -> Unit)? = null,
) : RecyclerView.Adapter<TaskSettingAdapter.ViewHolder>(), ItemMoveCallback.Listener { ) : RecyclerView.Adapter<TaskSettingAdapter.ViewHolder>(), ItemMoveCallback.Listener {
private lateinit var touchHelper: ItemTouchHelper private lateinit var touchHelper: ItemTouchHelper
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val view = LayoutInflater.from(parent.context) val view = LayoutInflater.from(parent.context).inflate(R.layout.adapter_task_setting_item, parent, false)
.inflate(R.layout.adapter_task_setting_item, parent, false)
return ViewHolder(view) return ViewHolder(view)
} }
@ -48,8 +49,17 @@ class TaskSettingAdapter(
private val dragIcon: ImageView = itemView.findViewById(R.id.iv_drag) private val dragIcon: ImageView = itemView.findViewById(R.id.iv_drag)
init { init {
editIcon.setOnClickListener(this) if (removeClickListener == null) {
removeIcon.setOnClickListener(this) removeIcon.visibility = View.GONE
} else {
removeIcon.setOnClickListener(this)
}
if (editClickListener == null) {
editIcon.visibility = View.GONE
} else {
editIcon.setOnClickListener(this)
}
dragIcon.setOnTouchListener { _, event -> dragIcon.setOnTouchListener { _, event ->
if (event.actionMasked == MotionEvent.ACTION_DOWN) { if (event.actionMasked == MotionEvent.ACTION_DOWN) {
@ -69,8 +79,8 @@ class TaskSettingAdapter(
val position = adapterPosition val position = adapterPosition
if (position != RecyclerView.NO_POSITION) { if (position != RecyclerView.NO_POSITION) {
when (v?.id) { when (v?.id) {
R.id.iv_edit -> editClickListener(position) R.id.iv_edit -> editClickListener?.let { it(position) }
R.id.iv_remove -> removeClickListener(position) R.id.iv_remove -> removeClickListener?.let { it(position) }
} }
} }
} }
@ -79,11 +89,11 @@ class TaskSettingAdapter(
override fun onItemMove(fromPosition: Int, toPosition: Int) { override fun onItemMove(fromPosition: Int, toPosition: Int) {
if (fromPosition < toPosition) { if (fromPosition < toPosition) {
for (i in fromPosition until toPosition) { for (i in fromPosition until toPosition) {
itemList[i] = itemList.set(i + 1, itemList[i]) Collections.swap(itemList, i, i + 1)
} }
} else { } else {
for (i in fromPosition downTo toPosition + 1) { for (i in fromPosition downTo toPosition + 1) {
itemList[i] = itemList.set(i - 1, itemList[i]) Collections.swap(itemList, i, i - 1)
} }
} }
notifyItemMoved(fromPosition, toPosition) notifyItemMoved(fromPosition, toPosition)
@ -91,40 +101,3 @@ class TaskSettingAdapter(
override fun onDragFinished() {} 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()
}
}
}

View File

@ -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()
}
}
}

View File

@ -71,7 +71,7 @@ class FrpcSpinnerAdapter<T> : BaseEditSpinnerAdapter<T>, EditSpinnerFilter {
} else { } else {
holder = convertView.tag as ViewHolder 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.iconView.setImageDrawable(item.icon)
holder.statusView.setImageDrawable( holder.statusView.setImageDrawable(
getDrawable( getDrawable(

View File

@ -3,29 +3,29 @@ package com.idormy.sms.forwarder.adapter.spinner
import android.graphics.drawable.Drawable import android.graphics.drawable.Drawable
@Suppress("unused") @Suppress("unused")
class FrpcAdapterItem( class FrpcSpinnerItem(
var title: CharSequence, var title: CharSequence,
var icon: Drawable? = null, var icon: Drawable? = null,
var uid: String = "", var uid: String = "",
var autorun: Int? = 1 var autorun: Int? = 1
) { ) {
fun setTitle(title: CharSequence): FrpcAdapterItem { fun setTitle(title: CharSequence): FrpcSpinnerItem {
this.title = title this.title = title
return this return this
} }
fun setIcon(icon: Drawable?): FrpcAdapterItem { fun setIcon(icon: Drawable?): FrpcSpinnerItem {
this.icon = icon this.icon = icon
return this return this
} }
fun setUid(uid: String): FrpcAdapterItem { fun setUid(uid: String): FrpcSpinnerItem {
this.uid = uid this.uid = uid
return this return this
} }
fun setAutorun(autorun: Int): FrpcAdapterItem { fun setAutorun(autorun: Int): FrpcSpinnerItem {
this.autorun = autorun this.autorun = autorun
return this return this
} }
@ -37,13 +37,13 @@ class FrpcAdapterItem(
companion object { companion object {
@JvmStatic @JvmStatic
fun of(title: CharSequence): FrpcAdapterItem { fun of(title: CharSequence): FrpcSpinnerItem {
return FrpcAdapterItem(title) return FrpcSpinnerItem(title)
} }
@JvmStatic @JvmStatic
fun arrayOf(vararg titles: CharSequence): Array<FrpcAdapterItem> { fun arrayOf(vararg titles: CharSequence): Array<FrpcSpinnerItem> {
return titles.map { FrpcAdapterItem(it) }.toTypedArray() return titles.map { FrpcSpinnerItem(it) }.toTypedArray()
} }
} }
} }

View File

@ -71,7 +71,7 @@ class RuleSpinnerAdapter<T> : BaseEditSpinnerAdapter<T>, EditSpinnerFilter {
} else { } else {
holder = convertView.tag as ViewHolder 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.iconView.setImageDrawable(item.icon)
holder.statusView.setImageDrawable( holder.statusView.setImageDrawable(
getDrawable( getDrawable(

View File

@ -3,29 +3,29 @@ package com.idormy.sms.forwarder.adapter.spinner
import android.graphics.drawable.Drawable import android.graphics.drawable.Drawable
@Suppress("unused") @Suppress("unused")
class RuleAdapterItem( class RuleSpinnerItem(
var title: CharSequence, var title: CharSequence,
var icon: Drawable? = null, var icon: Drawable? = null,
var id: Long? = 0L, var id: Long? = 0L,
var status: Int? = 1 var status: Int? = 1
) { ) {
fun setTitle(title: CharSequence): RuleAdapterItem { fun setTitle(title: CharSequence): RuleSpinnerItem {
this.title = title this.title = title
return this return this
} }
fun setIcon(icon: Drawable?): RuleAdapterItem { fun setIcon(icon: Drawable?): RuleSpinnerItem {
this.icon = icon this.icon = icon
return this return this
} }
fun setId(id: Long): RuleAdapterItem { fun setId(id: Long): RuleSpinnerItem {
this.id = id this.id = id
return this return this
} }
fun setStatus(status: Int): RuleAdapterItem { fun setStatus(status: Int): RuleSpinnerItem {
this.status = status this.status = status
return this return this
} }
@ -37,13 +37,13 @@ class RuleAdapterItem(
companion object { companion object {
@JvmStatic @JvmStatic
fun of(title: CharSequence): RuleAdapterItem { fun of(title: CharSequence): RuleSpinnerItem {
return RuleAdapterItem(title) return RuleSpinnerItem(title)
} }
@JvmStatic @JvmStatic
fun arrayOf(vararg titles: CharSequence): Array<RuleAdapterItem> { fun arrayOf(vararg titles: CharSequence): Array<RuleSpinnerItem> {
return titles.map { RuleAdapterItem(it) }.toTypedArray() return titles.map { RuleSpinnerItem(it) }.toTypedArray()
} }
} }
} }

View File

@ -71,7 +71,7 @@ class SenderSpinnerAdapter<T> : BaseEditSpinnerAdapter<T>, EditSpinnerFilter {
} else { } else {
holder = convertView.tag as ViewHolder 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.iconView.setImageDrawable(item.icon)
holder.statusView.setImageDrawable( holder.statusView.setImageDrawable(
getDrawable( getDrawable(

View File

@ -3,29 +3,29 @@ package com.idormy.sms.forwarder.adapter.spinner
import android.graphics.drawable.Drawable import android.graphics.drawable.Drawable
@Suppress("unused") @Suppress("unused")
class SenderAdapterItem( class SenderSpinnerItem(
var title: CharSequence, var title: CharSequence,
var icon: Drawable? = null, var icon: Drawable? = null,
var id: Long? = 0L, var id: Long? = 0L,
var status: Int? = 1 var status: Int? = 1
) { ) {
fun setTitle(title: CharSequence): SenderAdapterItem { fun setTitle(title: CharSequence): SenderSpinnerItem {
this.title = title this.title = title
return this return this
} }
fun setIcon(icon: Drawable?): SenderAdapterItem { fun setIcon(icon: Drawable?): SenderSpinnerItem {
this.icon = icon this.icon = icon
return this return this
} }
fun setId(id: Long): SenderAdapterItem { fun setId(id: Long): SenderSpinnerItem {
this.id = id this.id = id
return this return this
} }
fun setStatus(status: Int): SenderAdapterItem { fun setStatus(status: Int): SenderSpinnerItem {
this.status = status this.status = status
return this return this
} }
@ -37,13 +37,13 @@ class SenderAdapterItem(
companion object { companion object {
@JvmStatic @JvmStatic
fun of(title: CharSequence): SenderAdapterItem { fun of(title: CharSequence): SenderSpinnerItem {
return SenderAdapterItem(title) return SenderSpinnerItem(title)
} }
@JvmStatic @JvmStatic
fun arrayOf(vararg titles: CharSequence): Array<SenderAdapterItem> { fun arrayOf(vararg titles: CharSequence): Array<SenderSpinnerItem> {
return titles.map { SenderAdapterItem(it) }.toTypedArray() return titles.map { SenderSpinnerItem(it) }.toTypedArray()
} }
} }
} }

View File

@ -40,8 +40,9 @@ interface FrpcDao {
@Query("SELECT * FROM Frpc where autorun=1") @Query("SELECT * FROM Frpc where autorun=1")
fun getAutorun(): List<Frpc> fun getAutorun(): List<Frpc>
@Query("SELECT * FROM Frpc WHERE uid IN (:uids)") //使用 ORDER BY 子句和 instr() 函数按照列表中 uid 的顺序返回结果
fun getByUids(uids: List<String>): List<Frpc> @Query("SELECT * FROM Frpc WHERE uid IN (:uids) ORDER BY instr(:instr, uid)")
fun getByUids(uids: List<String>, instr: String): List<Frpc>
@Query("SELECT * FROM Frpc ORDER BY time DESC") @Query("SELECT * FROM Frpc ORDER BY time DESC")
fun pagingSource(): PagingSource<Int, Frpc> fun pagingSource(): PagingSource<Int, Frpc>

View File

@ -41,8 +41,9 @@ interface SenderDao {
@Query("SELECT * FROM Sender where id=:id") @Query("SELECT * FROM Sender where id=:id")
fun getOne(id: Long): Sender fun getOne(id: Long): Sender
@Query("SELECT * FROM Sender WHERE id IN (:ids)") //使用 ORDER BY 子句和 instr() 函数按照列表中 ID 的顺序返回结果
fun getByIds(ids: List<Long>): List<Sender> @Query("SELECT * FROM Sender WHERE id IN (:ids) ORDER BY instr(:instr, id)")
fun getByIds(ids: List<Long>, instr: String): List<Sender>
@Query("SELECT count(*) FROM Sender where type=:type and status=:status") @Query("SELECT count(*) FROM Sender where type=:type and status=:status")
fun count(type: String, status: Int): Single<Int> fun count(type: String, status: Int): Single<Int>

View File

@ -8,7 +8,7 @@ class ConvertersSenderList {
@TypeConverter @TypeConverter
fun stringToObject(value: String): List<Sender> { fun stringToObject(value: String): List<Sender> {
return Core.sender.getByIds(value.split(",").map { it.trim().toLong() }) return Core.sender.getByIds(value.split(",").map { it.trim().toLong() }, value)
} }
@TypeConverter @TypeConverter

View File

@ -31,6 +31,6 @@ class FrpcRepository(private val frpcDao: FrpcDao) {
fun getAutorun(): List<Frpc> = frpcDao.getAutorun() fun getAutorun(): List<Frpc> = frpcDao.getAutorun()
fun getByUids(uids: List<String>): List<Frpc> = frpcDao.getByUids(uids) fun getByUids(uids: List<String>, instr: String): List<Frpc> = frpcDao.getByUids(uids, instr)
} }

View File

@ -30,7 +30,7 @@ class SenderRepository(private val senderDao: SenderDao) {
fun getOne(id: Long) = senderDao.getOne(id) fun getOne(id: Long) = senderDao.getOne(id)
fun getByIds(ids: List<Long>) = senderDao.getByIds(ids) fun getByIds(ids: List<Long>, instr: String) = senderDao.getByIds(ids, instr)
fun getAllNonCache(): List<Sender> { fun getAllNonCache(): List<Sender> {
val query = SimpleSQLiteQuery("SELECT * FROM Sender ORDER BY id ASC") val query = SimpleSQLiteQuery("SELECT * FROM Sender ORDER BY id ASC")

View File

@ -7,14 +7,19 @@ import android.view.ViewGroup
import android.widget.* import android.widget.*
import androidx.fragment.app.viewModels import androidx.fragment.app.viewModels
import androidx.lifecycle.Observer 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.OneTimeWorkRequestBuilder
import androidx.work.WorkManager import androidx.work.WorkManager
import com.idormy.sms.forwarder.App import com.idormy.sms.forwarder.App
import com.idormy.sms.forwarder.R 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.AppListAdapterItem
import com.idormy.sms.forwarder.adapter.spinner.AppListSpinnerAdapter 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.SenderSpinnerAdapter
import com.idormy.sms.forwarder.adapter.spinner.SenderSpinnerItem
import com.idormy.sms.forwarder.core.BaseFragment import com.idormy.sms.forwarder.core.BaseFragment
import com.idormy.sms.forwarder.core.Core import com.idormy.sms.forwarder.core.Core
import com.idormy.sms.forwarder.database.entity.Rule import com.idormy.sms.forwarder.database.entity.Rule
@ -74,16 +79,17 @@ class RulesEditFragment : BaseFragment<FragmentRulesEditBinding?>(), View.OnClic
private var silentPeriodStart = 0 private var silentPeriodStart = 0
private var silentPeriodEnd = 0 private var silentPeriodEnd = 0
//当前发送通道 //所有发送通道下拉框
private var senderId = 0L private var senderListAll = mutableListOf<Sender>()
private var senderListSelected: MutableList<Sender> = mutableListOf() private val senderSpinnerList = mutableListOf<SenderSpinnerItem>()
private var senderItemMap = HashMap<Long, LinearLayout>(2)
//发送通道列表
private var senderListAll: MutableList<Sender> = mutableListOf()
private val senderSpinnerList = ArrayList<SenderAdapterItem>()
private lateinit var senderSpinnerAdapter: SenderSpinnerAdapter<*> private lateinit var senderSpinnerAdapter: SenderSpinnerAdapter<*>
//已选发送通道列表
private var senderId = 0L
private var senderListSelected = mutableListOf<Sender>()
private lateinit var senderRecyclerView: RecyclerView
private lateinit var senderRecyclerAdapter: SenderRecyclerAdapter
//已安装App信息列表 //已安装App信息列表
private val appListSpinnerList = ArrayList<AppListAdapterItem>() private val appListSpinnerList = ArrayList<AppListAdapterItem>()
private lateinit var appListSpinnerAdapter: AppListSpinnerAdapter<*> private lateinit var appListSpinnerAdapter: AppListSpinnerAdapter<*>
@ -400,49 +406,16 @@ class RulesEditFragment : BaseFragment<FragmentRulesEditBinding?>(), View.OnClic
} }
//初始化发送通道下拉框 //初始化发送通道下拉框
@SuppressLint("SetTextI18n") @SuppressLint("SetTextI18n", "NotifyDataSetChanged")
private fun initSenderSpinner() { private fun initSenderSpinner() {
//免打扰(禁用转发)时间段 //免打扰(禁用转发)时间段
binding!!.tvSilentPeriod.text = mTimeOption[silentPeriodStart] + " ~ " + mTimeOption[silentPeriodEnd] binding!!.tvSilentPeriod.text = mTimeOption[silentPeriodStart] + " ~ " + mTimeOption[silentPeriodEnd]
Core.sender.getAll().subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(object : SingleObserver<List<Sender>> { //初始化发送通道下拉框
override fun onSubscribe(d: Disposable) {}
override fun onError(e: Throwable) {
e.printStackTrace()
Log.e(TAG, e.toString())
}
override fun onSuccess(senderList: List<Sender>) {
if (senderList.isEmpty()) {
XToastUtils.error(R.string.add_sender_first)
return
}
senderListAll = senderList as MutableList<Sender>
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 -> binding!!.spSender.setOnItemClickListener { _: AdapterView<*>, _: View, position: Int, _: Long ->
try { try {
val sender = senderSpinnerAdapter.getItemSource(position) as SenderAdapterItem val item = senderSpinnerAdapter.getItemSource(position) as SenderSpinnerItem
senderId = sender.id!! senderId = item.id!!
if (senderId > 0L) { if (senderId > 0L) {
senderListSelected.forEach { senderListSelected.forEach {
if (senderId == it.id) { if (senderId == it.id) {
@ -453,11 +426,14 @@ class RulesEditFragment : BaseFragment<FragmentRulesEditBinding?>(), View.OnClic
senderListAll.forEach { senderListAll.forEach {
if (senderId == it.id) { if (senderId == it.id) {
senderListSelected.add(it) 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)) XToastUtils.warning(getString(R.string.sender_disabled_tips))
} }
} }
@ -465,60 +441,80 @@ class RulesEditFragment : BaseFragment<FragmentRulesEditBinding?>(), View.OnClic
XToastUtils.error(e.message.toString()) 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 private fun getSenderList() {
* Core.sender.getAll().subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(object : SingleObserver<List<Sender>> {
* @param senderItemMap 管理item的map用于删除指定header override fun onSubscribe(d: Disposable) {}
* @param layoutSenders 需要挂载item的LinearLayout
* @param sender SenderAdapterItem
*/
@SuppressLint("SetTextI18n")
private fun addSenderItemLinearLayout(
senderItemMap: MutableMap<Long, LinearLayout>, layoutSenders: LinearLayout, sender: SenderAdapterItem
) {
val layoutSenderItem = View.inflate(requireContext(), R.layout.item_add_sender, null) as LinearLayout
val ivRemoveSender = layoutSenderItem.findViewById<ImageView>(R.id.iv_remove_sender)
val ivSenderImage = layoutSenderItem.findViewById<ImageView>(R.id.iv_sender_image)
val ivSenderStatus = layoutSenderItem.findViewById<ImageView>(R.id.iv_sender_status)
val tvSenderName = layoutSenderItem.findViewById<TextView>(R.id.tv_sender_name)
ivSenderImage.setImageDrawable(sender.icon) override fun onError(e: Throwable) {
ivSenderStatus.setImageDrawable(getDrawable(if (STATUS_OFF == sender.status) R.drawable.ic_stop else R.drawable.ic_start)) e.printStackTrace()
val senderItemId = sender.id as Long Log.e(TAG, "getSenderList error: ${e.message}")
tvSenderName.text = "ID-$senderItemId${sender.title}" }
ivRemoveSender.tag = senderItemId @SuppressLint("NotifyDataSetChanged")
ivRemoveSender.setOnClickListener { view2: View -> override fun onSuccess(senderList: List<Sender>) {
val tagId = view2.tag as Long if (senderList.isEmpty()) {
layoutSenders.removeView(senderItemMap[tagId]) XToastUtils.error(R.string.add_sender_first)
senderItemMap.remove(tagId) return
//senderListSelected.removeIf { it.id == tagId }
for (it in senderListSelected) {
if (it.id == tagId) {
senderListSelected -= it
break
} }
senderSpinnerList.clear()
senderListAll = senderList as MutableList<Sender>
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下拉列表 //初始化APP下拉列表
@ -581,6 +577,7 @@ class RulesEditFragment : BaseFragment<FragmentRulesEditBinding?>(), View.OnClic
rule.senderList.forEach { rule.senderList.forEach {
senderId = it.id senderId = it.id
senderListSelected.add(it) senderListSelected.add(it)
Log.d(TAG, it.toString())
} }
if (isClone) { if (isClone) {
@ -591,6 +588,7 @@ class RulesEditFragment : BaseFragment<FragmentRulesEditBinding?>(), View.OnClic
} }
Log.d(TAG, rule.toString()) Log.d(TAG, rule.toString())
checkSenderLogicShow()
binding!!.rgSenderLogic.check(rule.getSenderLogicCheckId()) binding!!.rgSenderLogic.check(rule.getSenderLogicCheckId())
binding!!.rgSimSlot.check(rule.getSimSlotCheckId()) binding!!.rgSimSlot.check(rule.getSimSlotCheckId())
binding!!.rgFiled.check(rule.getFiledCheckId()) binding!!.rgFiled.check(rule.getFiledCheckId())
@ -619,6 +617,15 @@ class RulesEditFragment : BaseFragment<FragmentRulesEditBinding?>(), 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 { private fun checkForm(): Rule {
if (senderListSelected.isEmpty() || senderId == 0L) { if (senderListSelected.isEmpty() || senderId == 0L) {
@ -677,9 +684,7 @@ class RulesEditFragment : BaseFragment<FragmentRulesEditBinding?>(), View.OnClic
else -> CHECK_SIM_SLOT_ALL else -> CHECK_SIM_SLOT_ALL
} }
val status = if (binding!!.sbStatus.isChecked) STATUS_ON else STATUS_OFF 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( return Rule(
ruleId, ruleId,
ruleType, ruleType,

View File

@ -13,9 +13,9 @@ import com.google.android.material.bottomsheet.BottomSheetDialog
import com.google.gson.Gson import com.google.gson.Gson
import com.idormy.sms.forwarder.App import com.idormy.sms.forwarder.App
import com.idormy.sms.forwarder.R 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.TaskSettingAdapter
import com.idormy.sms.forwarder.adapter.WidgetItemAdapter 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.BaseFragment
import com.idormy.sms.forwarder.core.Core import com.idormy.sms.forwarder.core.Core
import com.idormy.sms.forwarder.database.entity.Task import com.idormy.sms.forwarder.database.entity.Task
@ -50,7 +50,7 @@ import java.util.*
@Page(name = "自动任务·编辑器") @Page(name = "自动任务·编辑器")
@Suppress("PrivatePropertyName", "DEPRECATION", "UNUSED_PARAMETER", "EmptyMethod") @Suppress("PrivatePropertyName", "DEPRECATION", "UNUSED_PARAMETER", "EmptyMethod", "NotifyDataSetChanged")
class TasksEditFragment : BaseFragment<FragmentTasksEditBinding?>(), View.OnClickListener, RecyclerViewHolder.OnItemClickListener<PageInfo> { class TasksEditFragment : BaseFragment<FragmentTasksEditBinding?>(), View.OnClickListener, RecyclerViewHolder.OnItemClickListener<PageInfo> {
private val TAG: String = TasksEditFragment::class.java.simpleName private val TAG: String = TasksEditFragment::class.java.simpleName
@ -70,14 +70,13 @@ class TasksEditFragment : BaseFragment<FragmentTasksEditBinding?>(), View.OnClic
@AutoWired(name = KEY_TASK_CLONE) @AutoWired(name = KEY_TASK_CLONE)
var isClone: Boolean = false var isClone: Boolean = false
private lateinit var recyclerConditions: RecyclerView private lateinit var conditionsRecyclerView: RecyclerView
private lateinit var recyclerActions: RecyclerView
private lateinit var conditionsAdapter: TaskSettingAdapter private lateinit var conditionsAdapter: TaskSettingAdapter
private lateinit var actionsAdapter: TaskSettingAdapter private var conditionsList = mutableListOf<TaskSetting>()
private var itemListConditions = mutableListOf<TaskSetting>() private lateinit var actionsRecyclerView: RecyclerView
private var itemListActions = mutableListOf<TaskSetting>() private lateinit var actionsAdapter: TaskSettingAdapter
private var actionsList = mutableListOf<TaskSetting>()
private var TASK_CONDITION_FRAGMENT_LIST = listOf( private var TASK_CONDITION_FRAGMENT_LIST = listOf(
PageInfo( PageInfo(
@ -218,8 +217,8 @@ class TasksEditFragment : BaseFragment<FragmentTasksEditBinding?>(), View.OnClic
* 初始化控件 * 初始化控件
*/ */
override fun initViews() { override fun initViews() {
recyclerConditions = findViewById(R.id.recycler_conditions) conditionsRecyclerView = findViewById(R.id.recycler_conditions)
recyclerActions = findViewById(R.id.recycler_actions) actionsRecyclerView = findViewById(R.id.recycler_actions)
// 初始化 RecyclerView 和 Adapter // 初始化 RecyclerView 和 Adapter
initRecyclerViews() initRecyclerViews()
@ -229,34 +228,38 @@ class TasksEditFragment : BaseFragment<FragmentTasksEditBinding?>(), View.OnClic
override fun onItemMove(fromPosition: Int, toPosition: Int) { override fun onItemMove(fromPosition: Int, toPosition: Int) {
Log.d(TAG, "onItemMove: $fromPosition $toPosition") Log.d(TAG, "onItemMove: $fromPosition $toPosition")
conditionsAdapter.onItemMove(fromPosition, toPosition) conditionsAdapter.onItemMove(fromPosition, toPosition)
conditionsList = conditionsAdapter.itemList
} }
override fun onDragFinished() { override fun onDragFinished() {
//itemListConditions保持与adapter一致 //conditionsList保持与adapter一致
itemListConditions = conditionsAdapter.itemList.toMutableList() conditionsList = conditionsAdapter.itemList
Log.d(TAG, "onItemMove: $itemListConditions") Log.d(TAG, "onDragFinished: $conditionsList")
//conditionsAdapter.notifyDataSetChanged()
} }
}) })
val itemTouchHelperConditions = ItemTouchHelper(conditionsCallback) val itemTouchHelperConditions = ItemTouchHelper(conditionsCallback)
itemTouchHelperConditions.attachToRecyclerView(recyclerConditions) itemTouchHelperConditions.attachToRecyclerView(conditionsRecyclerView)
conditionsAdapter.setTouchHelper(itemTouchHelperConditions) conditionsAdapter.setTouchHelper(itemTouchHelperConditions)
val actionsCallback = ItemMoveCallback(object : ItemMoveCallback.Listener { val actionsCallback = ItemMoveCallback(object : ItemMoveCallback.Listener {
override fun onItemMove(fromPosition: Int, toPosition: Int) { override fun onItemMove(fromPosition: Int, toPosition: Int) {
Log.d(TAG, "onItemMove: $fromPosition $toPosition") Log.d(TAG, "onItemMove: $fromPosition $toPosition")
actionsAdapter.onItemMove(fromPosition, toPosition) actionsAdapter.onItemMove(fromPosition, toPosition)
actionsList = actionsAdapter.itemList
} }
override fun onDragFinished() { override fun onDragFinished() {
//itemListActions保持与adapter一致 //actionsList保持与adapter一致
itemListActions = actionsAdapter.itemList.toMutableList() actionsList = actionsAdapter.itemList
Log.d(TAG, "onItemMove: $itemListActions") Log.d(TAG, "onDragFinished: $actionsList")
//actionsAdapter.notifyDataSetChanged()
} }
}) })
val itemTouchHelperActions = ItemTouchHelper(actionsCallback) val itemTouchHelperActions = ItemTouchHelper(actionsCallback)
itemTouchHelperActions.attachToRecyclerView(recyclerActions) itemTouchHelperActions.attachToRecyclerView(actionsRecyclerView)
actionsAdapter.setTouchHelper(itemTouchHelperActions) actionsAdapter.setTouchHelper(itemTouchHelperActions)
if (taskId <= 0) { //新增 if (taskId <= 0) { //新增
@ -284,7 +287,7 @@ class TasksEditFragment : BaseFragment<FragmentTasksEditBinding?>(), View.OnClic
R.id.layout_add_condition -> { R.id.layout_add_condition -> {
val bottomSheet: View = LayoutInflater.from(requireContext()).inflate(R.layout.dialog_task_condition_bottom_sheet, null) val bottomSheet: View = LayoutInflater.from(requireContext()).inflate(R.layout.dialog_task_condition_bottom_sheet, null)
val tvTitle: TextView = bottomSheet.findViewById(R.id.tv_title) 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) val recyclerView: RecyclerView = bottomSheet.findViewById(R.id.recyclerView)
WidgetUtils.initGridRecyclerView(recyclerView, 4, DensityUtils.dp2px(1f)) WidgetUtils.initGridRecyclerView(recyclerView, 4, DensityUtils.dp2px(1f))
@ -386,20 +389,20 @@ class TasksEditFragment : BaseFragment<FragmentTasksEditBinding?>(), View.OnClic
if (task.conditions.isNotEmpty()) { if (task.conditions.isNotEmpty()) {
val conditionList = Gson().fromJson(task.conditions, Array<TaskSetting>::class.java).toMutableList() val conditionList = Gson().fromJson(task.conditions, Array<TaskSetting>::class.java).toMutableList()
for (condition in conditionList) { 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() 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()) { if (task.actions.isNotEmpty()) {
val actionList = Gson().fromJson(task.actions, Array<TaskSetting>::class.java).toMutableList() val actionList = Gson().fromJson(task.actions, Array<TaskSetting>::class.java).toMutableList()
for (action in actionList) { 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() 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) { } catch (e: Exception) {
e.printStackTrace() e.printStackTrace()
@ -416,10 +419,10 @@ class TasksEditFragment : BaseFragment<FragmentTasksEditBinding?>(), View.OnClic
if (taskName.isEmpty()) { if (taskName.isEmpty()) {
throw Exception("请输入任务名称") throw Exception("请输入任务名称")
} }
if (itemListConditions.size <= 0) { if (conditionsList.size <= 0) {
throw Exception("请添加触发条件") throw Exception("请添加触发条件")
} }
if (itemListActions.size <= 0) { if (actionsList.size <= 0) {
throw Exception("请添加执行动作") throw Exception("请添加执行动作")
} }
@ -427,7 +430,7 @@ class TasksEditFragment : BaseFragment<FragmentTasksEditBinding?>(), View.OnClic
// 将毫秒部分设置为 0避免因为毫秒部分不同导致的任务重复执行 // 将毫秒部分设置为 0避免因为毫秒部分不同导致的任务重复执行
lastExecTime.time = lastExecTime.time / 1000 * 1000 lastExecTime.time = lastExecTime.time / 1000 * 1000
var nextExecTime = lastExecTime var nextExecTime = lastExecTime
val firstCondition = itemListConditions[0] val firstCondition = conditionsList[0]
taskType = firstCondition.type taskType = firstCondition.type
when (taskType) { when (taskType) {
@ -445,13 +448,13 @@ class TasksEditFragment : BaseFragment<FragmentTasksEditBinding?>(), View.OnClic
//拼接任务描述 //拼接任务描述
val description = StringBuilder() val description = StringBuilder()
description.append(getString(R.string.task_conditions)).append(" ") 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(" ").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 val status = if (binding!!.sbStatus.isChecked) STATUS_ON else STATUS_OFF
return Task( 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<FragmentTasksEditBinding?>(), View.OnClic
if (widgetInfo.classPath.contains(".condition.")) { if (widgetInfo.classPath.contains(".condition.")) {
val typeCondition = pos + KEY_BACK_CODE_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不可改变 //注意TASK_CONDITION_XXX 枚举值 等于 TASK_CONDITION_FRAGMENT_LIST 索引加上 KEY_BACK_CODE_CONDITION不可改变
if (item.type == typeCondition) { if (item.type == typeCondition) {
XToastUtils.error(getString(R.string.condition_already_exists)) XToastUtils.error(getString(R.string.condition_already_exists))
@ -514,7 +517,7 @@ class TasksEditFragment : BaseFragment<FragmentTasksEditBinding?>(), View.OnClic
} else { } else {
val typeAction = pos + KEY_BACK_CODE_ACTION 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不可改变 //注意TASK_ACTION_XXX 枚举值 等于 TASK_ACTION_FRAGMENT_LIST 索引加上 KEY_BACK_CODE_ACTION不可改变
if (item.type == typeAction) { if (item.type == typeAction) {
XToastUtils.error(getString(R.string.action_already_exists)) XToastUtils.error(getString(R.string.action_already_exists))
@ -549,15 +552,17 @@ class TasksEditFragment : BaseFragment<FragmentTasksEditBinding?>(), View.OnClic
val widgetInfo = TASK_CONDITION_FRAGMENT_LIST[widgetInfoIndex] val widgetInfo = TASK_CONDITION_FRAGMENT_LIST[widgetInfoIndex]
description = extras.getString(KEY_BACK_DESCRIPTION_CONDITION) ?: widgetInfo.name.toString() description = extras.getString(KEY_BACK_DESCRIPTION_CONDITION) ?: widgetInfo.name.toString()
val taskSetting = TaskSetting(resultCode, widgetInfo.name, description, setting, requestCode) val taskSetting = TaskSetting(resultCode, widgetInfo.name, description, setting, requestCode)
//requestCode: 等于 itemListConditions 的索引加1 //requestCode: 等于 conditionsList 的索引加1
if (requestCode == 0) { if (requestCode == 0) {
taskSetting.position = itemListConditions.size taskSetting.position = conditionsList.size
itemListConditions.add(taskSetting) conditionsList.add(taskSetting)
conditionsAdapter.notifyItemInserted(conditionsList.size - 1)
} else { } else {
itemListConditions[requestCode - 1] = taskSetting conditionsList[requestCode - 1] = taskSetting
conditionsAdapter.notifyItemChanged(requestCode - 1)
} }
conditionsAdapter.notifyDataSetChanged() //conditionsAdapter.notifyDataSetChanged()
binding!!.layoutAddCondition.visibility = if (itemListConditions.size >= MAX_SETTING_NUM) View.GONE else View.VISIBLE 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) { } else if (resultCode in KEY_BACK_CODE_ACTION..KEY_BACK_CODE_ACTION + 999) {
setting = extras.getString(KEY_BACK_DATA_ACTION) ?: return setting = extras.getString(KEY_BACK_DATA_ACTION) ?: return
//注意TASK_ACTION_XXX 枚举值 等于 TASK_ACTION_FRAGMENT_LIST 索引加上 KEY_BACK_CODE_ACTION不可改变 //注意TASK_ACTION_XXX 枚举值 等于 TASK_ACTION_FRAGMENT_LIST 索引加上 KEY_BACK_CODE_ACTION不可改变
@ -566,31 +571,33 @@ class TasksEditFragment : BaseFragment<FragmentTasksEditBinding?>(), View.OnClic
val widgetInfo = TASK_ACTION_FRAGMENT_LIST[widgetInfoIndex] val widgetInfo = TASK_ACTION_FRAGMENT_LIST[widgetInfoIndex]
description = extras.getString(KEY_BACK_DESCRIPTION_ACTION) ?: widgetInfo.name.toString() description = extras.getString(KEY_BACK_DESCRIPTION_ACTION) ?: widgetInfo.name.toString()
val taskSetting = TaskSetting(resultCode, widgetInfo.name, description, setting, requestCode) val taskSetting = TaskSetting(resultCode, widgetInfo.name, description, setting, requestCode)
//requestCode: 等于 itemListActions 的索引加1 //requestCode: 等于 actionsList 的索引加1
if (requestCode == 0) { if (requestCode == 0) {
taskSetting.position = itemListActions.size taskSetting.position = actionsList.size
itemListActions.add(taskSetting) actionsList.add(taskSetting)
actionsAdapter.notifyItemInserted(actionsList.size - 1)
} else { } else {
itemListActions[requestCode - 1] = taskSetting actionsList[requestCode - 1] = taskSetting
actionsAdapter.notifyItemChanged(requestCode - 1)
} }
actionsAdapter.notifyDataSetChanged() //actionsAdapter.notifyDataSetChanged()
binding!!.layoutAddAction.visibility = if (itemListActions.size >= MAX_SETTING_NUM) View.GONE else View.VISIBLE binding!!.layoutAddAction.visibility = if (actionsList.size >= MAX_SETTING_NUM) View.GONE else View.VISIBLE
} }
Log.d(TAG, "requestCode:$requestCode resultCode:$resultCode setting:$setting") Log.d(TAG, "requestCode:$requestCode resultCode:$resultCode setting:$setting")
} }
} }
private fun initRecyclerViews() { 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()) layoutManager = LinearLayoutManager(requireContext())
adapter = conditionsAdapter adapter = conditionsAdapter
} }
recyclerActions.apply { actionsRecyclerView.apply {
layoutManager = LinearLayoutManager(requireContext()) layoutManager = LinearLayoutManager(requireContext())
adapter = actionsAdapter adapter = actionsAdapter
} }
@ -599,42 +606,48 @@ class TasksEditFragment : BaseFragment<FragmentTasksEditBinding?>(), View.OnClic
private fun editCondition(position: Int) { private fun editCondition(position: Int) {
// 实现编辑条件项目的逻辑 // 实现编辑条件项目的逻辑
// 根据 position 对特定项目进行编辑 // 根据 position 对特定项目进行编辑
val condition = itemListConditions[position] val condition = conditionsList[position]
Log.d(TAG, "editCondition: $position, $condition") Log.d(TAG, "editCondition: $position, $condition")
val widgetInfoIndex = condition.type - KEY_BACK_CODE_CONDITION val widgetInfoIndex = condition.type - KEY_BACK_CODE_CONDITION
//判断是否存在 //判断是否存在
if (widgetInfoIndex < 0 || widgetInfoIndex >= TASK_CONDITION_FRAGMENT_LIST.size) return if (widgetInfoIndex < 0 || widgetInfoIndex >= TASK_CONDITION_FRAGMENT_LIST.size) return
val widgetInfo = TASK_CONDITION_FRAGMENT_LIST[condition.type - KEY_BACK_CODE_CONDITION] val widgetInfo = TASK_CONDITION_FRAGMENT_LIST[condition.type - KEY_BACK_CODE_CONDITION]
@Suppress("UNCHECKED_CAST") PageOption.to(Class.forName(widgetInfo.classPath) as Class<XPageFragment>) //跳转的fragment @Suppress("UNCHECKED_CAST")
.setRequestCode(position + 1) //requestCode: 0 新增 、>0 编辑itemListConditions 的索引加1 PageOption.to(Class.forName(widgetInfo.classPath) as Class<XPageFragment>) //跳转的fragment
.putString(KEY_EVENT_DATA_CONDITION, condition.setting).open(this) .setRequestCode(position + 1) //requestCode: 0 新增 、>0 编辑conditionsList 的索引加1
.putString(KEY_EVENT_DATA_CONDITION, condition.setting)
.open(this)
} }
private fun removeCondition(position: Int) { private fun removeCondition(position: Int) {
itemListConditions.removeAt(position) conditionsList.removeAt(position)
conditionsAdapter.notifyItemRemoved(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) { private fun editAction(position: Int) {
// 实现编辑操作项目的逻辑 // 实现编辑操作项目的逻辑
// 根据 position 对特定项目进行编辑 // 根据 position 对特定项目进行编辑
val action = itemListActions[position] val action = actionsList[position]
Log.d(TAG, "editAction: $position, $action") Log.d(TAG, "editAction: $position, $action")
val widgetInfoIndex = action.type - KEY_BACK_CODE_ACTION val widgetInfoIndex = action.type - KEY_BACK_CODE_ACTION
//判断是否存在 //判断是否存在
if (widgetInfoIndex < 0 || widgetInfoIndex >= TASK_ACTION_FRAGMENT_LIST.size) return if (widgetInfoIndex < 0 || widgetInfoIndex >= TASK_ACTION_FRAGMENT_LIST.size) return
val widgetInfo = TASK_ACTION_FRAGMENT_LIST[action.type - KEY_BACK_CODE_ACTION] val widgetInfo = TASK_ACTION_FRAGMENT_LIST[action.type - KEY_BACK_CODE_ACTION]
@Suppress("UNCHECKED_CAST") PageOption.to(Class.forName(widgetInfo.classPath) as Class<XPageFragment>) //跳转的fragment @Suppress("UNCHECKED_CAST")
.setRequestCode(position + 1) //requestCode: 0 新增 、>0 编辑itemListActions 的索引加1 PageOption.to(Class.forName(widgetInfo.classPath) as Class<XPageFragment>) //跳转的fragment
.putString(KEY_EVENT_DATA_ACTION, action.setting).open(this) .setRequestCode(position + 1) //requestCode: 0 新增 、>0 编辑actionsList 的索引加1
.putString(KEY_EVENT_DATA_ACTION, action.setting)
.open(this)
} }
private fun removeAction(position: Int) { private fun removeAction(position: Int) {
itemListActions.removeAt(position) actionsList.removeAt(position)
actionsAdapter.notifyItemRemoved(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
} }
} }

View File

@ -6,13 +6,15 @@ import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.AdapterView import android.widget.AdapterView
import android.widget.ImageView import androidx.recyclerview.widget.ItemTouchHelper
import android.widget.LinearLayout import androidx.recyclerview.widget.LinearLayoutManager
import android.widget.TextView import androidx.recyclerview.widget.RecyclerView
import com.google.gson.Gson import com.google.gson.Gson
import com.idormy.sms.forwarder.R 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.FrpcSpinnerAdapter
import com.idormy.sms.forwarder.adapter.spinner.FrpcSpinnerItem
import com.idormy.sms.forwarder.core.BaseFragment import com.idormy.sms.forwarder.core.BaseFragment
import com.idormy.sms.forwarder.core.Core import com.idormy.sms.forwarder.core.Core
import com.idormy.sms.forwarder.database.entity.Frpc 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_EVENT_DATA_ACTION
import com.idormy.sms.forwarder.utils.KEY_TEST_ACTION import com.idormy.sms.forwarder.utils.KEY_TEST_ACTION
import com.idormy.sms.forwarder.utils.Log 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.TASK_ACTION_FRPC
import com.idormy.sms.forwarder.utils.XToastUtils import com.idormy.sms.forwarder.utils.XToastUtils
import com.jeremyliao.liveeventbus.LiveEventBus import com.jeremyliao.liveeventbus.LiveEventBus
@ -47,16 +48,17 @@ class FrpcFragment : BaseFragment<FragmentTasksActionFrpcBinding?>(), View.OnCli
private var titleBar: TitleBar? = null private var titleBar: TitleBar? = null
private var mCountDownHelper: CountDownButtonHelper? = null private var mCountDownHelper: CountDownButtonHelper? = null
//当前发送通道 //所有Frpc下拉框
private var frpcUid = "" private var frpcListAll = mutableListOf<Frpc>()
private var frpcListSelected: MutableList<Frpc> = mutableListOf() private val frpcSpinnerList = mutableListOf<FrpcSpinnerItem>()
private var frpcItemMap = HashMap<String, LinearLayout>(2)
//发送通道列表
private var frpcListAll: MutableList<Frpc> = mutableListOf()
private val frpcSpinnerList = ArrayList<FrpcAdapterItem>()
private lateinit var frpcSpinnerAdapter: FrpcSpinnerAdapter<*> private lateinit var frpcSpinnerAdapter: FrpcSpinnerAdapter<*>
//已选Frpc列表
private var frpcUid = ""
private var frpcListSelected = mutableListOf<Frpc>()
private lateinit var frpcRecyclerView: RecyclerView
private lateinit var frpcRecyclerAdapter: FrpcRecyclerAdapter
@JvmField @JvmField
@AutoWired(name = KEY_EVENT_DATA_ACTION) @AutoWired(name = KEY_EVENT_DATA_ACTION)
var eventData: String? = null var eventData: String? = null
@ -101,7 +103,7 @@ class FrpcFragment : BaseFragment<FragmentTasksActionFrpcBinding?>(), View.OnCli
} }
//初始化发送通道下拉框 //初始化发送通道下拉框
initFrpcSpinner() initFrpc()
} }
@SuppressLint("SetTextI18n") @SuppressLint("SetTextI18n")
@ -162,47 +164,14 @@ class FrpcFragment : BaseFragment<FragmentTasksActionFrpcBinding?>(), View.OnCli
} }
} }
//初始化发送通道下拉框 //初始化Frpc
@SuppressLint("SetTextI18n") @SuppressLint("SetTextI18n", "NotifyDataSetChanged")
private fun initFrpcSpinner() { private fun initFrpc() {
Core.frpc.getAll().subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(object : SingleObserver<List<Frpc>> { //初始化Frpc下拉框
override fun onSubscribe(d: Disposable) {}
override fun onError(e: Throwable) {
e.printStackTrace()
Log.e(TAG, "initFrpcSpinner error: ${e.message}")
}
override fun onSuccess(frpcList: List<Frpc>) {
if (frpcList.isEmpty()) {
XToastUtils.error(R.string.add_frpc_first)
return
}
frpcListAll = frpcList as MutableList<Frpc>
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)
}
}
}
}
}
})
binding!!.spFrpc.setOnItemClickListener { _: AdapterView<*>, _: View, position: Int, _: Long -> binding!!.spFrpc.setOnItemClickListener { _: AdapterView<*>, _: View, position: Int, _: Long ->
try { try {
val frpc = frpcSpinnerAdapter.getItemSource(position) as FrpcAdapterItem val item = frpcSpinnerAdapter.getItemSource(position) as FrpcSpinnerItem
frpcUid = frpc.uid frpcUid = item.uid
if (frpcUid.isNotEmpty()) { if (frpcUid.isNotEmpty()) {
frpcListSelected.forEach { frpcListSelected.forEach {
if (frpcUid == it.uid) { if (frpcUid == it.uid) {
@ -213,60 +182,87 @@ class FrpcFragment : BaseFragment<FragmentTasksActionFrpcBinding?>(), View.OnCli
frpcListAll.forEach { frpcListAll.forEach {
if (frpcUid == it.uid) { if (frpcUid == it.uid) {
frpcListSelected.add(it) frpcListSelected.add(it)
addFrpcItemLinearLayout(frpcItemMap, binding!!.layoutFrpcs, frpc)
} }
} }
frpcRecyclerAdapter.notifyDataSetChanged()
/*if (STATUS_OFF == frpc.status) {
XToastUtils.warning(getString(R.string.frpc_disabled_tips))
}*/
} }
} catch (e: Exception) { } catch (e: Exception) {
XToastUtils.error(e.message.toString()) 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列表
* 动态增删Frpc private fun getFrpcList() {
* Core.frpc.getAll().subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(object : SingleObserver<List<Frpc>> {
* @param frpcItemMap 管理item的map用于删除指定header override fun onSubscribe(d: Disposable) {}
* @param layoutFrpcs 需要挂载item的LinearLayout
* @param frpc FrpcAdapterItem
*/
@SuppressLint("SetTextI18n")
private fun addFrpcItemLinearLayout(
frpcItemMap: MutableMap<String, LinearLayout>, layoutFrpcs: LinearLayout, frpc: FrpcAdapterItem
) {
val layoutFrpcItem = View.inflate(requireContext(), R.layout.item_add_frpc, null) as LinearLayout
val ivRemoveFrpc = layoutFrpcItem.findViewById<ImageView>(R.id.iv_remove_frpc)
val ivFrpcImage = layoutFrpcItem.findViewById<ImageView>(R.id.iv_frpc_image)
val ivFrpcStatus = layoutFrpcItem.findViewById<ImageView>(R.id.iv_frpc_status)
val tvFrpcName = layoutFrpcItem.findViewById<TextView>(R.id.tv_frpc_name)
ivFrpcImage.setImageDrawable(frpc.icon) override fun onError(e: Throwable) {
ivFrpcStatus.setImageDrawable(getDrawable(if (STATUS_ON == frpc.autorun) R.drawable.ic_autorun else R.drawable.ic_manual)) e.printStackTrace()
val frpcItemId = frpc.uid Log.e(TAG, "getFrpcList error: ${e.message}")
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
}
} }
Log.d(TAG, frpcListSelected.count().toString())
Log.d(TAG, frpcListSelected.toString()) @SuppressLint("NotifyDataSetChanged")
if (frpcListSelected.isEmpty()) frpcUid = "" override fun onSuccess(frpcList: List<Frpc>) {
} if (frpcList.isEmpty()) {
layoutFrpcs.addView(layoutFrpcItem) XToastUtils.error(R.string.add_frpc_first)
frpcItemMap[frpcItemId] = layoutFrpcItem return
}
frpcSpinnerList.clear()
frpcListAll = frpcList as MutableList<Frpc>
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()
}
})
} }
//检查设置 //检查设置

View File

@ -6,10 +6,15 @@ import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.* 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.google.gson.Gson
import com.idormy.sms.forwarder.R 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.SenderSpinnerAdapter
import com.idormy.sms.forwarder.adapter.spinner.SenderSpinnerItem
import com.idormy.sms.forwarder.core.BaseFragment import com.idormy.sms.forwarder.core.BaseFragment
import com.idormy.sms.forwarder.core.Core import com.idormy.sms.forwarder.core.Core
import com.idormy.sms.forwarder.database.entity.Rule import com.idormy.sms.forwarder.database.entity.Rule
@ -52,16 +57,17 @@ class NotificationFragment : BaseFragment<FragmentTasksActionNotificationBinding
private var silentPeriodStart = 0 private var silentPeriodStart = 0
private var silentPeriodEnd = 0 private var silentPeriodEnd = 0
//当前发送通道 //所有发送通道下拉框
var senderId = 0L private var senderListAll = mutableListOf<Sender>()
var senderListSelected: MutableList<Sender> = mutableListOf() private val senderSpinnerList = mutableListOf<SenderSpinnerItem>()
private var senderItemMap = HashMap<Long, LinearLayout>(2)
//发送通道列表
var senderListAll: MutableList<Sender> = mutableListOf()
private val senderSpinnerList = ArrayList<SenderAdapterItem>()
private lateinit var senderSpinnerAdapter: SenderSpinnerAdapter<*> private lateinit var senderSpinnerAdapter: SenderSpinnerAdapter<*>
//已选发送通道列表
private var senderId = 0L
private var senderListSelected = mutableListOf<Sender>()
private lateinit var sendersRecyclerView: RecyclerView
private lateinit var senderRecyclerAdapter: SenderRecyclerAdapter
private var ruleId: Long = 0 private var ruleId: Long = 0
private var ruleType: String = "app" private var ruleType: String = "app"
@ -109,6 +115,7 @@ class NotificationFragment : BaseFragment<FragmentTasksActionNotificationBinding
senderListSelected.add(it) senderListSelected.add(it)
} }
checkSenderLogicShow()
binding!!.rgSenderLogic.check(settingVo.getSenderLogicCheckId()) binding!!.rgSenderLogic.check(settingVo.getSenderLogicCheckId())
if (!TextUtils.isEmpty(settingVo.smsTemplate.trim())) { if (!TextUtils.isEmpty(settingVo.smsTemplate.trim())) {
binding!!.sbSmsTemplate.isChecked = true binding!!.sbSmsTemplate.isChecked = true
@ -293,49 +300,16 @@ class NotificationFragment : BaseFragment<FragmentTasksActionNotificationBinding
} }
//初始化发送通道下拉框 //初始化发送通道下拉框
@SuppressLint("SetTextI18n") @SuppressLint("SetTextI18n", "NotifyDataSetChanged")
private fun initSenderSpinner() { private fun initSenderSpinner() {
//免打扰(禁用转发)时间段 //免打扰(禁用转发)时间段
binding!!.tvSilentPeriod.text = mTimeOption[silentPeriodStart] + " ~ " + mTimeOption[silentPeriodEnd] binding!!.tvSilentPeriod.text = mTimeOption[silentPeriodStart] + " ~ " + mTimeOption[silentPeriodEnd]
Core.sender.getAll().subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(object : SingleObserver<List<Sender>> { //初始化发送通道下拉框
override fun onSubscribe(d: Disposable) {}
override fun onError(e: Throwable) {
e.printStackTrace()
Log.e(TAG, "initSenderSpinner error: ${e.message}")
}
override fun onSuccess(senderList: List<Sender>) {
if (senderList.isEmpty()) {
XToastUtils.error(R.string.add_sender_first)
return
}
senderListAll = senderList as MutableList<Sender>
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 -> binding!!.spSender.setOnItemClickListener { _: AdapterView<*>, _: View, position: Int, _: Long ->
try { try {
val sender = senderSpinnerAdapter.getItemSource(position) as SenderAdapterItem val item = senderSpinnerAdapter.getItemSource(position) as SenderSpinnerItem
senderId = sender.id!! senderId = item.id!!
if (senderId > 0L) { if (senderId > 0L) {
senderListSelected.forEach { senderListSelected.forEach {
if (senderId == it.id) { if (senderId == it.id) {
@ -346,11 +320,14 @@ class NotificationFragment : BaseFragment<FragmentTasksActionNotificationBinding
senderListAll.forEach { senderListAll.forEach {
if (senderId == it.id) { if (senderId == it.id) {
senderListSelected.add(it) 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)) XToastUtils.warning(getString(R.string.sender_disabled_tips))
} }
} }
@ -358,55 +335,84 @@ class NotificationFragment : BaseFragment<FragmentTasksActionNotificationBinding
XToastUtils.error(e.message.toString()) XToastUtils.error(e.message.toString())
} }
} }
// 初始化已选发送通道列表 RecyclerView 和 Adapter
sendersRecyclerView = binding!!.recyclerSenders
senderRecyclerAdapter = SenderRecyclerAdapter(senderListSelected, { position ->
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 private fun getSenderList() {
* Core.sender.getAll().subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(object : SingleObserver<List<Sender>> {
* @param senderItemMap 管理item的map用于删除指定header override fun onSubscribe(d: Disposable) {}
* @param layoutSenders 需要挂载item的LinearLayout
* @param sender SenderAdapterItem
*/
@SuppressLint("SetTextI18n")
private fun addSenderItemLinearLayout(
senderItemMap: MutableMap<Long, LinearLayout>, layoutSenders: LinearLayout, sender: SenderAdapterItem
) {
val layoutSenderItem = View.inflate(requireContext(), R.layout.item_add_sender, null) as LinearLayout
val ivRemoveSender = layoutSenderItem.findViewById<ImageView>(R.id.iv_remove_sender)
val ivSenderImage = layoutSenderItem.findViewById<ImageView>(R.id.iv_sender_image)
val ivSenderStatus = layoutSenderItem.findViewById<ImageView>(R.id.iv_sender_status)
val tvSenderName = layoutSenderItem.findViewById<TextView>(R.id.tv_sender_name)
ivSenderImage.setImageDrawable(sender.icon) override fun onError(e: Throwable) {
ivSenderStatus.setImageDrawable(getDrawable(if (STATUS_OFF == sender.status) R.drawable.ic_stop else R.drawable.ic_start)) e.printStackTrace()
val senderItemId = sender.id as Long Log.e(TAG, "getSenderList error: ${e.message}")
tvSenderName.text = "ID-$senderItemId${sender.title}" }
ivRemoveSender.tag = senderItemId @SuppressLint("NotifyDataSetChanged")
ivRemoveSender.setOnClickListener { view2: View -> override fun onSuccess(senderList: List<Sender>) {
val tagId = view2.tag as Long if (senderList.isEmpty()) {
layoutSenders.removeView(senderItemMap[tagId]) XToastUtils.error(R.string.add_sender_first)
senderItemMap.remove(tagId) return
//senderListSelected.removeIf { it.id == tagId }
for (it in senderListSelected) {
if (it.id == tagId) {
senderListSelected -= it
break
} }
senderSpinnerList.clear()
senderListAll = senderList as MutableList<Sender>
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) { private fun checkSenderLogicShow() {
binding!!.layoutSenderLogic.visibility = View.VISIBLE if (senderListSelected.size > 1) {
} 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 binding!!.layoutSenderLogic.visibility = View.VISIBLE
} else { } else {
binding!!.layoutSenderLogic.visibility = View.GONE binding!!.layoutSenderLogic.visibility = View.GONE

View File

@ -6,27 +6,33 @@ import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.AdapterView import android.widget.AdapterView
import android.widget.ImageView import androidx.recyclerview.widget.ItemTouchHelper
import android.widget.LinearLayout import androidx.recyclerview.widget.LinearLayoutManager
import android.widget.TextView import androidx.recyclerview.widget.RecyclerView
import androidx.work.Data
import androidx.work.OneTimeWorkRequestBuilder
import androidx.work.WorkManager
import com.google.gson.Gson import com.google.gson.Gson
import com.idormy.sms.forwarder.R 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.RuleSpinnerAdapter
import com.idormy.sms.forwarder.adapter.spinner.RuleSpinnerItem
import com.idormy.sms.forwarder.core.BaseFragment import com.idormy.sms.forwarder.core.BaseFragment
import com.idormy.sms.forwarder.core.Core import com.idormy.sms.forwarder.core.Core
import com.idormy.sms.forwarder.database.entity.Rule import com.idormy.sms.forwarder.database.entity.Rule
import com.idormy.sms.forwarder.databinding.FragmentTasksActionRuleBinding 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.entity.action.RuleSetting
import com.idormy.sms.forwarder.utils.KEY_BACK_DATA_ACTION 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_BACK_DESCRIPTION_ACTION
import com.idormy.sms.forwarder.utils.KEY_EVENT_DATA_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.Log
import com.idormy.sms.forwarder.utils.STATUS_OFF
import com.idormy.sms.forwarder.utils.TASK_ACTION_RULE 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.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.xaop.annotation.SingleClick
import com.xuexiang.xpage.annotation.Page import com.xuexiang.xpage.annotation.Page
import com.xuexiang.xrouter.annotation.AutoWired import com.xuexiang.xrouter.annotation.AutoWired
@ -38,25 +44,27 @@ import io.reactivex.SingleObserver
import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.Disposable import io.reactivex.disposables.Disposable
import io.reactivex.schedulers.Schedulers import io.reactivex.schedulers.Schedulers
import java.util.Date
@Page(name = "Rule") @Page(name = "Rule")
@Suppress("PrivatePropertyName") @Suppress("PrivatePropertyName", "DEPRECATION")
class RuleFragment : BaseFragment<FragmentTasksActionRuleBinding?>(), View.OnClickListener { class RuleFragment : BaseFragment<FragmentTasksActionRuleBinding?>(), View.OnClickListener {
private val TAG: String = RuleFragment::class.java.simpleName private val TAG: String = RuleFragment::class.java.simpleName
private var titleBar: TitleBar? = null private var titleBar: TitleBar? = null
private var mCountDownHelper: CountDownButtonHelper? = null private var mCountDownHelper: CountDownButtonHelper? = null
//当前转发规则 //所有转发规则下拉框
private var ruleId = 0L private var ruleListAll = mutableListOf<Rule>()
private var ruleListSelected: MutableList<Rule> = mutableListOf() private val ruleSpinnerList = mutableListOf<RuleSpinnerItem>()
private var ruleItemMap = HashMap<Long, LinearLayout>(2)
//转发规则列表
private var ruleListAll: MutableList<Rule> = mutableListOf()
private val ruleSpinnerList = ArrayList<RuleAdapterItem>()
private lateinit var ruleSpinnerAdapter: RuleSpinnerAdapter<*> private lateinit var ruleSpinnerAdapter: RuleSpinnerAdapter<*>
//已选转发规则列表
private var ruleId = 0L
private var ruleListSelected = mutableListOf<Rule>()
private lateinit var ruleRecyclerView: RecyclerView
private lateinit var ruleRecyclerAdapter: RuleRecyclerAdapter
@JvmField @JvmField
@AutoWired(name = KEY_EVENT_DATA_ACTION) @AutoWired(name = KEY_EVENT_DATA_ACTION)
var eventData: String? = null var eventData: String? = null
@ -82,7 +90,7 @@ class RuleFragment : BaseFragment<FragmentTasksActionRuleBinding?>(), View.OnCli
*/ */
override fun initViews() { override fun initViews() {
//测试按钮增加倒计时,避免重复点击 //测试按钮增加倒计时,避免重复点击
mCountDownHelper = CountDownButtonHelper(binding!!.btnTest, 3) mCountDownHelper = CountDownButtonHelper(binding!!.btnTest, 1)
mCountDownHelper!!.setOnCountDownListener(object : CountDownButtonHelper.OnCountDownListener { mCountDownHelper!!.setOnCountDownListener(object : CountDownButtonHelper.OnCountDownListener {
override fun onCountDown(time: Int) { override fun onCountDown(time: Int) {
binding!!.btnTest.text = String.format(getString(R.string.seconds_n), time) binding!!.btnTest.text = String.format(getString(R.string.seconds_n), time)
@ -90,6 +98,8 @@ class RuleFragment : BaseFragment<FragmentTasksActionRuleBinding?>(), View.OnCli
override fun onFinished() { override fun onFinished() {
binding!!.btnTest.text = getString(R.string.test) binding!!.btnTest.text = getString(R.string.test)
//获取转发规则列表
getRuleList()
} }
}) })
@ -106,7 +116,7 @@ class RuleFragment : BaseFragment<FragmentTasksActionRuleBinding?>(), View.OnCli
} }
//初始化转发规则下拉框 //初始化转发规则下拉框
initRuleSpinner() initRule()
} }
@SuppressLint("SetTextI18n") @SuppressLint("SetTextI18n")
@ -114,15 +124,6 @@ class RuleFragment : BaseFragment<FragmentTasksActionRuleBinding?>(), View.OnCli
binding!!.btnTest.setOnClickListener(this) binding!!.btnTest.setOnClickListener(this)
binding!!.btnDel.setOnClickListener(this) binding!!.btnDel.setOnClickListener(this)
binding!!.btnSave.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 @SingleClick
@ -131,17 +132,21 @@ class RuleFragment : BaseFragment<FragmentTasksActionRuleBinding?>(), View.OnCli
when (v.id) { when (v.id) {
R.id.btn_test -> { R.id.btn_test -> {
mCountDownHelper?.start() mCountDownHelper?.start()
Thread { try {
try { val settingVo = checkSetting()
val settingVo = checkSetting() Log.d(TAG, settingVo.toString())
Log.d(TAG, settingVo.toString()) val taskAction = TaskSetting(TASK_ACTION_RULE, getString(R.string.task_rule), settingVo.description, Gson().toJson(settingVo), requestCode)
LiveEventBus.get(KEY_TEST_ACTION, String::class.java).post("success") val taskActionsJson = Gson().toJson(arrayListOf(taskAction))
} catch (e: Exception) { val msgInfo = MsgInfo("task", getString(R.string.task_rule), settingVo.description, Date(), getString(R.string.task_rule))
LiveEventBus.get(KEY_TEST_ACTION, String::class.java).post(e.message.toString()) val actionData = Data.Builder().putLong(TaskWorker.taskId, 0).putString(TaskWorker.taskActions, taskActionsJson).putString(TaskWorker.msgInfo, Gson().toJson(msgInfo)).build()
e.printStackTrace() val actionRequest = OneTimeWorkRequestBuilder<ActionWorker>().setInputData(actionData).build()
Log.e(TAG, "onClick error: ${e.message}") WorkManager.getInstance().enqueue(actionRequest)
} } catch (e: Exception) {
}.start() mCountDownHelper?.finish()
e.printStackTrace()
Log.e(TAG, "onClick error: ${e.message}")
XToastUtils.error(e.message.toString(), 30000)
}
return return
} }
@ -167,53 +172,14 @@ class RuleFragment : BaseFragment<FragmentTasksActionRuleBinding?>(), View.OnCli
} }
} }
//初始化转发规则下拉框 //初始化转发规则
@SuppressLint("SetTextI18n") @SuppressLint("SetTextI18n", "NotifyDataSetChanged")
private fun initRuleSpinner() { private fun initRule() {
Core.rule.getAll().subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(object : SingleObserver<List<Rule>> { //初始化转发规则下拉框
override fun onSubscribe(d: Disposable) {}
override fun onError(e: Throwable) {
e.printStackTrace()
Log.e(TAG, "initRuleSpinner error: ${e.message}")
}
override fun onSuccess(ruleList: List<Rule>) {
if (ruleList.isEmpty()) {
XToastUtils.error(R.string.add_rule_first)
return
}
ruleListAll = ruleList as MutableList<Rule>
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)
}
}
}
}
}
})
binding!!.spRule.setOnItemClickListener { _: AdapterView<*>, _: View, position: Int, _: Long -> binding!!.spRule.setOnItemClickListener { _: AdapterView<*>, _: View, position: Int, _: Long ->
try { try {
val rule = ruleSpinnerAdapter.getItemSource(position) as RuleAdapterItem val item = ruleSpinnerAdapter.getItemSource(position) as RuleSpinnerItem
ruleId = rule.id!! ruleId = item.id!!
if (ruleId > 0L) { if (ruleId > 0L) {
ruleListSelected.forEach { ruleListSelected.forEach {
if (ruleId == it.id) { if (ruleId == it.id) {
@ -224,67 +190,100 @@ class RuleFragment : BaseFragment<FragmentTasksActionRuleBinding?>(), View.OnCli
ruleListAll.forEach { ruleListAll.forEach {
if (ruleId == it.id) { if (ruleId == it.id) {
ruleListSelected.add(it) ruleListSelected.add(it)
addRuleItemLinearLayout(ruleItemMap, binding!!.layoutRules, rule)
} }
} }
ruleRecyclerAdapter.notifyDataSetChanged()
/*if (STATUS_OFF == rule.status) {
XToastUtils.warning(getString(R.string.rule_disabled_tips))
}*/
} }
} catch (e: Exception) { } catch (e: Exception) {
XToastUtils.error(e.message.toString()) 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 private fun getRuleList() {
* Core.rule.getAll().subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(object : SingleObserver<List<Rule>> {
* @param ruleItemMap 管理item的map用于删除指定header override fun onSubscribe(d: Disposable) {}
* @param layoutRules 需要挂载item的LinearLayout
* @param rule RuleAdapterItem
*/
@SuppressLint("SetTextI18n")
private fun addRuleItemLinearLayout(
ruleItemMap: MutableMap<Long, LinearLayout>, layoutRules: LinearLayout, rule: RuleAdapterItem
) {
val layoutRuleItem = View.inflate(requireContext(), R.layout.item_add_rule, null) as LinearLayout
val ivRemoveRule = layoutRuleItem.findViewById<ImageView>(R.id.iv_remove_rule)
val ivRuleImage = layoutRuleItem.findViewById<ImageView>(R.id.iv_rule_image)
val ivRuleStatus = layoutRuleItem.findViewById<ImageView>(R.id.iv_rule_status)
val tvRuleName = layoutRuleItem.findViewById<TextView>(R.id.tv_rule_name)
ivRuleImage.setImageDrawable(rule.icon) override fun onError(e: Throwable) {
ivRuleStatus.setImageDrawable(getDrawable(if (STATUS_OFF == rule.status) R.drawable.ic_stop else R.drawable.ic_start)) e.printStackTrace()
val ruleItemId = rule.id as Long Log.e(TAG, "getRuleList error: ${e.message}")
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
}
} }
Log.d(TAG, ruleListSelected.count().toString())
Log.d(TAG, ruleListSelected.toString()) @SuppressLint("NotifyDataSetChanged")
if (ruleListSelected.isEmpty()) ruleId = 0L override fun onSuccess(ruleList: List<Rule>) {
} if (ruleList.isEmpty()) {
layoutRules.addView(layoutRuleItem) XToastUtils.error(R.string.add_rule_first)
ruleItemMap[ruleItemId] = layoutRuleItem return
}
ruleSpinnerList.clear()
ruleListAll = ruleList as MutableList<Rule>
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") @SuppressLint("SetTextI18n")
private fun checkSetting(): RuleSetting { private fun checkSetting(): RuleSetting {
if (ruleListSelected.isEmpty() || ruleId == 0L) { if (ruleListSelected.isEmpty() || ruleId == 0L) {
throw Exception(getString(R.string.new_sender_first)) throw Exception(getString(R.string.new_rule_first))
} }
val description = StringBuilder() val description = StringBuilder()

View File

@ -6,27 +6,33 @@ import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.AdapterView import android.widget.AdapterView
import android.widget.ImageView import androidx.recyclerview.widget.ItemTouchHelper
import android.widget.LinearLayout import androidx.recyclerview.widget.LinearLayoutManager
import android.widget.TextView import androidx.recyclerview.widget.RecyclerView
import androidx.work.Data
import androidx.work.OneTimeWorkRequestBuilder
import androidx.work.WorkManager
import com.google.gson.Gson import com.google.gson.Gson
import com.idormy.sms.forwarder.R 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.SenderSpinnerAdapter
import com.idormy.sms.forwarder.adapter.spinner.SenderSpinnerItem
import com.idormy.sms.forwarder.core.BaseFragment import com.idormy.sms.forwarder.core.BaseFragment
import com.idormy.sms.forwarder.core.Core import com.idormy.sms.forwarder.core.Core
import com.idormy.sms.forwarder.database.entity.Sender import com.idormy.sms.forwarder.database.entity.Sender
import com.idormy.sms.forwarder.databinding.FragmentTasksActionSenderBinding 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.entity.action.SenderSetting
import com.idormy.sms.forwarder.utils.KEY_BACK_DATA_ACTION 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_BACK_DESCRIPTION_ACTION
import com.idormy.sms.forwarder.utils.KEY_EVENT_DATA_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.Log
import com.idormy.sms.forwarder.utils.STATUS_OFF
import com.idormy.sms.forwarder.utils.TASK_ACTION_SENDER 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.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.xaop.annotation.SingleClick
import com.xuexiang.xpage.annotation.Page import com.xuexiang.xpage.annotation.Page
import com.xuexiang.xrouter.annotation.AutoWired import com.xuexiang.xrouter.annotation.AutoWired
@ -38,25 +44,27 @@ import io.reactivex.SingleObserver
import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.Disposable import io.reactivex.disposables.Disposable
import io.reactivex.schedulers.Schedulers import io.reactivex.schedulers.Schedulers
import java.util.Date
@Page(name = "Sender") @Page(name = "Sender")
@Suppress("PrivatePropertyName") @Suppress("PrivatePropertyName", "DEPRECATION")
class SenderFragment : BaseFragment<FragmentTasksActionSenderBinding?>(), View.OnClickListener { class SenderFragment : BaseFragment<FragmentTasksActionSenderBinding?>(), View.OnClickListener {
private val TAG: String = SenderFragment::class.java.simpleName private val TAG: String = SenderFragment::class.java.simpleName
private var titleBar: TitleBar? = null private var titleBar: TitleBar? = null
private var mCountDownHelper: CountDownButtonHelper? = null private var mCountDownHelper: CountDownButtonHelper? = null
//当前发送通道 //所有发送通道下拉框
private var senderId = 0L private var senderListAll = mutableListOf<Sender>()
private var senderListSelected: MutableList<Sender> = mutableListOf() private val senderSpinnerList = mutableListOf<SenderSpinnerItem>()
private var senderItemMap = HashMap<Long, LinearLayout>(2)
//发送通道列表
private var senderListAll: MutableList<Sender> = mutableListOf()
private val senderSpinnerList = ArrayList<SenderAdapterItem>()
private lateinit var senderSpinnerAdapter: SenderSpinnerAdapter<*> private lateinit var senderSpinnerAdapter: SenderSpinnerAdapter<*>
//已选发送通道列表
private var senderId = 0L
private var senderListSelected = mutableListOf<Sender>()
private lateinit var senderRecyclerView: RecyclerView
private lateinit var senderRecyclerAdapter: SenderRecyclerAdapter
@JvmField @JvmField
@AutoWired(name = KEY_EVENT_DATA_ACTION) @AutoWired(name = KEY_EVENT_DATA_ACTION)
var eventData: String? = null var eventData: String? = null
@ -80,9 +88,10 @@ class SenderFragment : BaseFragment<FragmentTasksActionSenderBinding?>(), View.O
/** /**
* 初始化控件 * 初始化控件
*/ */
@SuppressLint("NotifyDataSetChanged")
override fun initViews() { override fun initViews() {
//测试按钮增加倒计时,避免重复点击 //测试按钮增加倒计时,避免重复点击
mCountDownHelper = CountDownButtonHelper(binding!!.btnTest, 3) mCountDownHelper = CountDownButtonHelper(binding!!.btnTest, 1)
mCountDownHelper!!.setOnCountDownListener(object : CountDownButtonHelper.OnCountDownListener { mCountDownHelper!!.setOnCountDownListener(object : CountDownButtonHelper.OnCountDownListener {
override fun onCountDown(time: Int) { override fun onCountDown(time: Int) {
binding!!.btnTest.text = String.format(getString(R.string.seconds_n), time) binding!!.btnTest.text = String.format(getString(R.string.seconds_n), time)
@ -90,6 +99,8 @@ class SenderFragment : BaseFragment<FragmentTasksActionSenderBinding?>(), View.O
override fun onFinished() { override fun onFinished() {
binding!!.btnTest.text = getString(R.string.test) binding!!.btnTest.text = getString(R.string.test)
//获取发送通道列表
getSenderList()
} }
}) })
@ -106,7 +117,7 @@ class SenderFragment : BaseFragment<FragmentTasksActionSenderBinding?>(), View.O
} }
//初始化发送通道下拉框 //初始化发送通道下拉框
initSenderSpinner() initSender()
} }
@SuppressLint("SetTextI18n") @SuppressLint("SetTextI18n")
@ -114,15 +125,6 @@ class SenderFragment : BaseFragment<FragmentTasksActionSenderBinding?>(), View.O
binding!!.btnTest.setOnClickListener(this) binding!!.btnTest.setOnClickListener(this)
binding!!.btnDel.setOnClickListener(this) binding!!.btnDel.setOnClickListener(this)
binding!!.btnSave.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 @SingleClick
@ -131,17 +133,21 @@ class SenderFragment : BaseFragment<FragmentTasksActionSenderBinding?>(), View.O
when (v.id) { when (v.id) {
R.id.btn_test -> { R.id.btn_test -> {
mCountDownHelper?.start() mCountDownHelper?.start()
Thread { try {
try { val settingVo = checkSetting()
val settingVo = checkSetting() Log.d(TAG, settingVo.toString())
Log.d(TAG, settingVo.toString()) val taskAction = TaskSetting(TASK_ACTION_SENDER, getString(R.string.task_sender), settingVo.description, Gson().toJson(settingVo), requestCode)
LiveEventBus.get(KEY_TEST_ACTION, String::class.java).post("success") val taskActionsJson = Gson().toJson(arrayListOf(taskAction))
} catch (e: Exception) { val msgInfo = MsgInfo("task", getString(R.string.task_sender), settingVo.description, Date(), getString(R.string.task_sender))
LiveEventBus.get(KEY_TEST_ACTION, String::class.java).post(e.message.toString()) val actionData = Data.Builder().putLong(TaskWorker.taskId, 0).putString(TaskWorker.taskActions, taskActionsJson).putString(TaskWorker.msgInfo, Gson().toJson(msgInfo)).build()
e.printStackTrace() val actionRequest = OneTimeWorkRequestBuilder<ActionWorker>().setInputData(actionData).build()
Log.e(TAG, "onClick error: ${e.message}") WorkManager.getInstance().enqueue(actionRequest)
} } catch (e: Exception) {
}.start() mCountDownHelper?.finish()
e.printStackTrace()
Log.e(TAG, "onClick error: ${e.message}")
XToastUtils.error(e.message.toString(), 30000)
}
return return
} }
@ -167,47 +173,14 @@ class SenderFragment : BaseFragment<FragmentTasksActionSenderBinding?>(), View.O
} }
} }
//初始化发送通道下拉框 //初始化发送通道
@SuppressLint("SetTextI18n") @SuppressLint("SetTextI18n", "NotifyDataSetChanged")
private fun initSenderSpinner() { private fun initSender() {
Core.sender.getAll().subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(object : SingleObserver<List<Sender>> { //初始化发送通道下拉框
override fun onSubscribe(d: Disposable) {}
override fun onError(e: Throwable) {
e.printStackTrace()
Log.e(TAG, "initSenderSpinner error: ${e.message}")
}
override fun onSuccess(senderList: List<Sender>) {
if (senderList.isEmpty()) {
XToastUtils.error(R.string.add_sender_first)
return
}
senderListAll = senderList as MutableList<Sender>
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 -> binding!!.spSender.setOnItemClickListener { _: AdapterView<*>, _: View, position: Int, _: Long ->
try { try {
val sender = senderSpinnerAdapter.getItemSource(position) as SenderAdapterItem val item = senderSpinnerAdapter.getItemSource(position) as SenderSpinnerItem
senderId = sender.id!! senderId = item.id!!
if (senderId > 0L) { if (senderId > 0L) {
senderListSelected.forEach { senderListSelected.forEach {
if (senderId == it.id) { if (senderId == it.id) {
@ -218,60 +191,87 @@ class SenderFragment : BaseFragment<FragmentTasksActionSenderBinding?>(), View.O
senderListAll.forEach { senderListAll.forEach {
if (senderId == it.id) { if (senderId == it.id) {
senderListSelected.add(it) senderListSelected.add(it)
addSenderItemLinearLayout(senderItemMap, binding!!.layoutSenders, sender)
} }
} }
senderRecyclerAdapter.notifyDataSetChanged()
/*if (STATUS_OFF == sender.status) {
XToastUtils.warning(getString(R.string.sender_disabled_tips))
}*/
} }
} catch (e: Exception) { } catch (e: Exception) {
XToastUtils.error(e.message.toString()) 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 private fun getSenderList() {
* Core.sender.getAll().subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(object : SingleObserver<List<Sender>> {
* @param senderItemMap 管理item的map用于删除指定header override fun onSubscribe(d: Disposable) {}
* @param layoutSenders 需要挂载item的LinearLayout
* @param sender SenderAdapterItem
*/
@SuppressLint("SetTextI18n")
private fun addSenderItemLinearLayout(
senderItemMap: MutableMap<Long, LinearLayout>, layoutSenders: LinearLayout, sender: SenderAdapterItem
) {
val layoutSenderItem = View.inflate(requireContext(), R.layout.item_add_sender, null) as LinearLayout
val ivRemoveSender = layoutSenderItem.findViewById<ImageView>(R.id.iv_remove_sender)
val ivSenderImage = layoutSenderItem.findViewById<ImageView>(R.id.iv_sender_image)
val ivSenderStatus = layoutSenderItem.findViewById<ImageView>(R.id.iv_sender_status)
val tvSenderName = layoutSenderItem.findViewById<TextView>(R.id.tv_sender_name)
ivSenderImage.setImageDrawable(sender.icon) override fun onError(e: Throwable) {
ivSenderStatus.setImageDrawable(getDrawable(if (STATUS_OFF == sender.status) R.drawable.ic_stop else R.drawable.ic_start)) e.printStackTrace()
val senderItemId = sender.id as Long Log.e(TAG, "getSenderList error: ${e.message}")
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
}
} }
Log.d(TAG, senderListSelected.count().toString())
Log.d(TAG, senderListSelected.toString()) @SuppressLint("NotifyDataSetChanged")
if (senderListSelected.isEmpty()) senderId = 0L override fun onSuccess(senderList: List<Sender>) {
} if (senderList.isEmpty()) {
layoutSenders.addView(layoutSenderItem) XToastUtils.error(R.string.add_sender_first)
senderItemMap[senderItemId] = layoutSenderItem return
}
senderSpinnerList.clear()
senderListAll = senderList as MutableList<Sender>
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<FragmentTasksActionSenderBinding?>(), View.O
return SenderSetting(description.toString(), status, senderListSelected) return SenderSetting(description.toString(), status, senderListSelected)
} }
} }

View File

@ -53,7 +53,7 @@ class SmsCommandUtils {
Core.frpc.getAutorun() Core.frpc.getAutorun()
} else { } else {
val uids = param.split(",") val uids = param.split(",")
Core.frpc.getByUids(uids) Core.frpc.getByUids(uids, param)
} }
if (frpcList.isEmpty()) { if (frpcList.isEmpty()) {

View File

@ -0,0 +1,87 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/xui_config_color_white"
android:orientation="vertical"
tools:ignore="UseCompoundDrawables">
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="?attr/xui_config_color_separator_light" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal"
android:paddingTop="@dimen/config_padding_5dp"
android:paddingBottom="@dimen/config_padding_5dp">
<LinearLayout
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_marginStart="5dp"
android:layout_marginEnd="5dp"
android:orientation="vertical">
<ImageView
android:id="@+id/iv_image"
android:layout_width="24dp"
android:layout_height="24dp"
android:src="@drawable/icon_dingtalk"
tools:ignore="ContentDescription" />
<ImageView
android:id="@+id/iv_status"
android:layout_width="10dp"
android:layout_height="10dp"
android:layout_marginStart="14dp"
android:layout_marginTop="-10dp"
android:src="@drawable/ic_stop"
tools:ignore="ContentDescription,VisualLintBounds" />
</LinearLayout>
<TextView
android:id="@+id/tv_title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="5dp"
android:layout_weight="1"
android:text="@string/dingtalk_robot"
android:textSize="@dimen/text_size_small"
android:textStyle="bold" />
<ImageView
android:id="@+id/iv_edit"
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_marginStart="5dp"
android:src="@drawable/ic_edit"
app:tint="@color/toast_info_color"
tools:ignore="ContentDescription,PrivateResource,ImageContrastCheck" />
<ImageView
android:id="@+id/iv_remove"
android:layout_width="20dp"
android:layout_height="20dp"
android:src="@drawable/ic_delete"
app:tint="@color/toast_error_color"
tools:ignore="ContentDescription,PrivateResource" />
<ImageView
android:id="@+id/iv_drag"
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_marginStart="5dp"
android:src="@drawable/ic_drag"
app:tint="@color/colorStart"
tools:ignore="ContentDescription,ImageContrastCheck" />
</LinearLayout>
</LinearLayout>

View File

@ -0,0 +1,87 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/xui_config_color_white"
android:orientation="vertical"
tools:ignore="UseCompoundDrawables">
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="?attr/xui_config_color_separator_light" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal"
android:paddingTop="@dimen/config_padding_5dp"
android:paddingBottom="@dimen/config_padding_5dp">
<LinearLayout
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_marginStart="5dp"
android:layout_marginEnd="5dp"
android:orientation="vertical">
<ImageView
android:id="@+id/iv_image"
android:layout_width="24dp"
android:layout_height="24dp"
android:src="@drawable/icon_dingtalk"
tools:ignore="ContentDescription" />
<ImageView
android:id="@+id/iv_status"
android:layout_width="10dp"
android:layout_height="10dp"
android:layout_marginStart="14dp"
android:layout_marginTop="-10dp"
android:src="@drawable/ic_stop"
tools:ignore="ContentDescription,VisualLintBounds" />
</LinearLayout>
<TextView
android:id="@+id/tv_title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="5dp"
android:layout_weight="1"
android:text="@string/dingtalk_robot"
android:textSize="@dimen/text_size_small"
android:textStyle="bold" />
<ImageView
android:id="@+id/iv_edit"
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_marginStart="5dp"
android:src="@drawable/ic_edit"
app:tint="@color/toast_info_color"
tools:ignore="ContentDescription,PrivateResource,ImageContrastCheck" />
<ImageView
android:id="@+id/iv_remove"
android:layout_width="20dp"
android:layout_height="20dp"
android:src="@drawable/ic_delete"
app:tint="@color/toast_error_color"
tools:ignore="ContentDescription,PrivateResource" />
<ImageView
android:id="@+id/iv_drag"
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_marginStart="5dp"
android:src="@drawable/ic_drag"
app:tint="@color/colorStart"
tools:ignore="ContentDescription,ImageContrastCheck" />
</LinearLayout>
</LinearLayout>

View File

@ -0,0 +1,87 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/xui_config_color_white"
android:orientation="vertical"
tools:ignore="UseCompoundDrawables">
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="?attr/xui_config_color_separator_light" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal"
android:paddingTop="@dimen/config_padding_5dp"
android:paddingBottom="@dimen/config_padding_5dp">
<LinearLayout
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_marginStart="5dp"
android:layout_marginEnd="5dp"
android:orientation="vertical">
<ImageView
android:id="@+id/iv_image"
android:layout_width="24dp"
android:layout_height="24dp"
android:src="@drawable/icon_dingtalk"
tools:ignore="ContentDescription" />
<ImageView
android:id="@+id/iv_status"
android:layout_width="10dp"
android:layout_height="10dp"
android:layout_marginStart="14dp"
android:layout_marginTop="-10dp"
android:src="@drawable/ic_stop"
tools:ignore="ContentDescription,VisualLintBounds" />
</LinearLayout>
<TextView
android:id="@+id/tv_title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="5dp"
android:layout_weight="1"
android:text="@string/dingtalk_robot"
android:textSize="@dimen/text_size_small"
android:textStyle="bold" />
<ImageView
android:id="@+id/iv_edit"
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_marginStart="5dp"
android:src="@drawable/ic_edit"
app:tint="@color/toast_info_color"
tools:ignore="ContentDescription,PrivateResource,ImageContrastCheck" />
<ImageView
android:id="@+id/iv_remove"
android:layout_width="20dp"
android:layout_height="20dp"
android:src="@drawable/ic_delete"
app:tint="@color/toast_error_color"
tools:ignore="ContentDescription,PrivateResource" />
<ImageView
android:id="@+id/iv_drag"
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_marginStart="5dp"
android:src="@drawable/ic_drag"
app:tint="@color/colorStart"
tools:ignore="ContentDescription,ImageContrastCheck" />
</LinearLayout>
</LinearLayout>

View File

@ -107,44 +107,22 @@
android:gravity="start" android:gravity="start"
android:maxEms="10" android:maxEms="10"
android:maxLines="1" android:maxLines="1"
android:text="@string/task_cron"
android:textSize="@dimen/text_size_medium" android:textSize="@dimen/text_size_medium"
android:textStyle="bold" /> android:textStyle="bold" />
<ImageView <TextView
android:id="@+id/iv_copy" android:id="@+id/tv_time"
android:layout_width="@dimen/card_view_image_size" android:layout_width="wrap_content"
android:layout_height="@dimen/card_view_image_size" android:layout_height="wrap_content"
android:padding="@dimen/card_view_image_padding" android:textSize="@dimen/text_size_small" />
android:scaleType="fitCenter"
android:src="@drawable/ic_copy"
app:tint="@color/colorStart"
tools:ignore="ContentDescription,ImageContrastCheck" />
<ImageView
android:id="@+id/iv_edit"
android:layout_width="@dimen/card_view_image_size"
android:layout_height="@dimen/card_view_image_size"
android:padding="@dimen/card_view_image_padding"
android:scaleType="fitCenter"
android:src="@drawable/ic_edit"
app:tint="@color/toast_info_color"
tools:ignore="ContentDescription,PrivateResource,ImageContrastCheck" />
<ImageView
android:id="@+id/iv_delete"
android:layout_width="@dimen/card_view_image_size"
android:layout_height="@dimen/card_view_image_size"
android:padding="@dimen/card_view_image_padding"
android:scaleType="fitCenter"
android:src="@drawable/ic_delete"
app:tint="@color/toast_error_color"
tools:ignore="ContentDescription,PrivateResource" />
</LinearLayout> </LinearLayout>
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:gravity="bottom"
android:orientation="horizontal"> android:orientation="horizontal">
<TextView <TextView
@ -155,16 +133,38 @@
android:ellipsize="end" android:ellipsize="end"
android:gravity="start" android:gravity="start"
android:maxLines="2" android:maxLines="2"
android:paddingEnd="@dimen/config_padding_10dp" android:text="@string/task_cron_tips"
android:textSize="@dimen/text_size_mini" android:textSize="@dimen/text_size_mini" />
tools:ignore="RtlSymmetry" />
<TextView <ImageView
android:id="@+id/tv_time" android:id="@+id/iv_copy"
android:layout_width="wrap_content" android:layout_width="22dp"
android:layout_height="wrap_content" android:layout_height="22dp"
android:layout_gravity="bottom|end" android:layout_marginStart="10dp"
android:textSize="@dimen/text_size_small" /> android:scaleType="fitCenter"
android:src="@drawable/ic_copy"
app:tint="@color/colorStart"
tools:ignore="ContentDescription,ImageContrastCheck" />
<ImageView
android:id="@+id/iv_edit"
android:layout_width="22dp"
android:layout_height="22dp"
android:layout_marginStart="4dp"
android:scaleType="fitCenter"
android:src="@drawable/ic_edit"
app:tint="@color/toast_info_color"
tools:ignore="ContentDescription,PrivateResource,ImageContrastCheck" />
<ImageView
android:id="@+id/iv_delete"
android:layout_width="22dp"
android:layout_height="22dp"
android:layout_marginStart="4dp"
android:scaleType="fitCenter"
android:src="@drawable/ic_delete"
app:tint="@color/toast_error_color"
tools:ignore="ContentDescription,PrivateResource" />
</LinearLayout> </LinearLayout>

View File

@ -50,11 +50,10 @@
</LinearLayout> </LinearLayout>
<LinearLayout <androidx.recyclerview.widget.RecyclerView
android:id="@+id/layout_Senders" android:id="@+id/recycler_senders"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content" />
android:orientation="vertical" />
<LinearLayout <LinearLayout
android:id="@+id/layout_sender_logic" android:id="@+id/layout_sender_logic"

View File

@ -18,13 +18,8 @@
android:orientation="vertical"> android:orientation="vertical">
<LinearLayout <LinearLayout
android:layout_width="match_parent" style="@style/BarTitleStyle"
android:layout_height="wrap_content" android:layout_marginTop="@dimen/config_margin_5dp">
android:layout_marginStart="5dp"
android:layout_marginTop="5dp"
android:layout_marginEnd="5dp"
android:gravity="center_vertical"
android:orientation="horizontal">
<TextView <TextView
android:layout_width="wrap_content" android:layout_width="wrap_content"
@ -331,14 +326,7 @@
</LinearLayout> </LinearLayout>
<LinearLayout <LinearLayout style="@style/BarTitleStyle">
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="5dp"
android:layout_marginTop="10dp"
android:layout_marginEnd="5dp"
android:gravity="center_vertical"
android:orientation="horizontal">
<TextView <TextView
android:layout_width="wrap_content" android:layout_width="wrap_content"
@ -873,14 +861,7 @@
</LinearLayout> </LinearLayout>
<LinearLayout <LinearLayout style="@style/BarTitleStyle">
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="5dp"
android:layout_marginTop="10dp"
android:layout_marginEnd="5dp"
android:gravity="center_vertical"
android:orientation="horizontal">
<TextView <TextView
android:layout_width="wrap_content" android:layout_width="wrap_content"
@ -1237,14 +1218,7 @@
</LinearLayout> </LinearLayout>
<LinearLayout <LinearLayout style="@style/BarTitleStyle">
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="5dp"
android:layout_marginTop="10dp"
android:layout_marginEnd="5dp"
android:gravity="center_vertical"
android:orientation="horizontal">
<TextView <TextView
android:layout_width="wrap_content" android:layout_width="wrap_content"

View File

@ -101,12 +101,11 @@
</LinearLayout> </LinearLayout>
<LinearLayout <androidx.recyclerview.widget.RecyclerView
android:id="@+id/layout_frpcs" android:id="@+id/recycler_frpcs"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="@dimen/config_margin_5dp" android:layout_marginTop="@dimen/config_margin_5dp" />
android:orientation="vertical" />
</LinearLayout> </LinearLayout>

View File

@ -59,11 +59,10 @@
</LinearLayout> </LinearLayout>
<LinearLayout <androidx.recyclerview.widget.RecyclerView
android:id="@+id/layout_senders" android:id="@+id/recycler_senders"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content" />
android:orientation="vertical" />
<LinearLayout <LinearLayout
android:id="@+id/layout_sender_logic" android:id="@+id/layout_sender_logic"

View File

@ -103,12 +103,11 @@
</LinearLayout> </LinearLayout>
<LinearLayout <androidx.recyclerview.widget.RecyclerView
android:id="@+id/layout_rules" android:id="@+id/recycler_rules"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="@dimen/config_margin_5dp" android:layout_marginTop="@dimen/config_margin_5dp" />
android:orientation="vertical" />
</LinearLayout> </LinearLayout>

View File

@ -103,12 +103,11 @@
</LinearLayout> </LinearLayout>
<LinearLayout <androidx.recyclerview.widget.RecyclerView
android:id="@+id/layout_senders" android:id="@+id/recycler_senders"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="@dimen/config_margin_5dp" android:layout_marginTop="@dimen/config_margin_5dp" />
android:orientation="vertical" />
</LinearLayout> </LinearLayout>

View File

@ -28,14 +28,7 @@
app:srcCompat="@drawable/auto_task_icon_settings" app:srcCompat="@drawable/auto_task_icon_settings"
tools:ignore="ImageContrastCheck" /> tools:ignore="ImageContrastCheck" />
<LinearLayout <LinearLayout style="@style/BarTitleStyle">
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="5dp"
android:layout_marginTop="5dp"
android:layout_marginEnd="5dp"
android:gravity="center_vertical"
android:orientation="horizontal">
<TextView <TextView
android:layout_width="wrap_content" android:layout_width="wrap_content"
@ -344,14 +337,7 @@
</LinearLayout> </LinearLayout>
<LinearLayout <LinearLayout style="@style/BarTitleStyle">
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="5dp"
android:layout_marginTop="10dp"
android:layout_marginEnd="5dp"
android:gravity="center_vertical"
android:orientation="horizontal">
<TextView <TextView
android:layout_width="wrap_content" android:layout_width="wrap_content"

View File

@ -187,7 +187,8 @@
android:layout_marginStart="@dimen/config_margin_10dp" android:layout_marginStart="@dimen/config_margin_10dp"
android:layout_marginEnd="@dimen/config_margin_10dp" android:layout_marginEnd="@dimen/config_margin_10dp"
android:gravity="center_vertical" android:gravity="center_vertical"
android:orientation="horizontal"> android:orientation="horizontal"
android:visibility="gone">
<TextView <TextView
android:layout_width="wrap_content" android:layout_width="wrap_content"

View File

@ -187,7 +187,8 @@
android:layout_marginStart="@dimen/config_margin_10dp" android:layout_marginStart="@dimen/config_margin_10dp"
android:layout_marginEnd="@dimen/config_margin_10dp" android:layout_marginEnd="@dimen/config_margin_10dp"
android:gravity="center_vertical" android:gravity="center_vertical"
android:orientation="horizontal"> android:orientation="horizontal"
android:visibility="gone">
<TextView <TextView
android:layout_width="wrap_content" android:layout_width="wrap_content"

View File

@ -51,14 +51,7 @@
</LinearLayout> </LinearLayout>
<LinearLayout <LinearLayout style="@style/BarTitleStyle.Task">
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="5dp"
android:layout_marginTop="20dp"
android:layout_marginEnd="5dp"
android:gravity="center_vertical"
android:orientation="horizontal">
<TextView <TextView
android:layout_width="wrap_content" android:layout_width="wrap_content"
@ -69,7 +62,7 @@
<TextView <TextView
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="15dp" android:layout_marginStart="10dp"
android:text="@string/task_conditions_tips" android:text="@string/task_conditions_tips"
android:textSize="@dimen/text_size_mini" android:textSize="@dimen/text_size_mini"
tools:ignore="SmallSp" /> tools:ignore="SmallSp" />
@ -137,14 +130,7 @@
</LinearLayout> </LinearLayout>
<LinearLayout <LinearLayout style="@style/BarTitleStyle.Task">
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="5dp"
android:layout_marginTop="20dp"
android:layout_marginEnd="5dp"
android:gravity="center_vertical"
android:orientation="horizontal">
<TextView <TextView
android:layout_width="wrap_content" android:layout_width="wrap_content"

View File

@ -1,68 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/xui_config_color_white"
android:orientation="vertical"
tools:ignore="UseCompoundDrawables">
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="?attr/xui_config_color_separator_light" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal">
<LinearLayout
android:layout_width="24dp"
android:layout_height="24dp"
android:orientation="vertical">
<ImageView
android:id="@+id/iv_frpc_image"
android:layout_width="24dp"
android:layout_height="24dp"
tools:ignore="ContentDescription,UseAppTint" />
<ImageView
android:id="@+id/iv_frpc_status"
android:layout_width="10dp"
android:layout_height="10dp"
android:layout_marginStart="14dp"
android:layout_marginTop="-10dp"
tools:ignore="ContentDescription" />
</LinearLayout>
<TextView
android:id="@+id/tv_frpc_name"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_weight="1"
android:ellipsize="marquee"
android:minHeight="?attr/ms_item_height_size"
android:paddingStart="?attr/ms_padding_left_size"
android:paddingTop="?attr/ms_padding_top_size"
android:paddingEnd="?attr/ms_padding_left_size"
android:paddingBottom="?attr/ms_padding_top_size"
android:singleLine="true"
tools:ignore="PrivateResource" />
<ImageView
android:id="@+id/iv_remove_frpc"
android:layout_width="18dp"
android:layout_height="18dp"
android:contentDescription="@string/frpc_del"
android:src="@drawable/ic_delete"
app:tint="#F15C58" />
</LinearLayout>
</LinearLayout>

View File

@ -1,68 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/xui_config_color_white"
android:orientation="vertical"
tools:ignore="UseCompoundDrawables">
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="?attr/xui_config_color_separator_light" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal">
<LinearLayout
android:layout_width="24dp"
android:layout_height="24dp"
android:orientation="vertical">
<ImageView
android:id="@+id/iv_rule_image"
android:layout_width="24dp"
android:layout_height="24dp"
tools:ignore="ContentDescription,UseAppTint" />
<ImageView
android:id="@+id/iv_rule_status"
android:layout_width="10dp"
android:layout_height="10dp"
android:layout_marginStart="14dp"
android:layout_marginTop="-10dp"
tools:ignore="ContentDescription" />
</LinearLayout>
<TextView
android:id="@+id/tv_rule_name"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_weight="1"
android:ellipsize="marquee"
android:minHeight="?attr/ms_item_height_size"
android:paddingStart="?attr/ms_padding_left_size"
android:paddingTop="?attr/ms_padding_top_size"
android:paddingEnd="?attr/ms_padding_left_size"
android:paddingBottom="?attr/ms_padding_top_size"
android:singleLine="true"
tools:ignore="PrivateResource" />
<ImageView
android:id="@+id/iv_remove_rule"
android:layout_width="18dp"
android:layout_height="18dp"
android:contentDescription="@string/rule_del"
android:src="@drawable/ic_delete"
app:tint="#F15C58" />
</LinearLayout>
</LinearLayout>

View File

@ -1,68 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/xui_config_color_white"
android:orientation="vertical"
tools:ignore="UseCompoundDrawables">
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="?attr/xui_config_color_separator_light" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal">
<LinearLayout
android:layout_width="24dp"
android:layout_height="24dp"
android:orientation="vertical">
<ImageView
android:id="@+id/iv_sender_image"
android:layout_width="24dp"
android:layout_height="24dp"
tools:ignore="ContentDescription" />
<ImageView
android:id="@+id/iv_sender_status"
android:layout_width="10dp"
android:layout_height="10dp"
android:layout_marginStart="14dp"
android:layout_marginTop="-10dp"
tools:ignore="ContentDescription" />
</LinearLayout>
<TextView
android:id="@+id/tv_sender_name"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_weight="1"
android:ellipsize="marquee"
android:minHeight="?attr/ms_item_height_size"
android:paddingStart="?attr/ms_padding_left_size"
android:paddingTop="?attr/ms_padding_top_size"
android:paddingEnd="?attr/ms_padding_left_size"
android:paddingBottom="?attr/ms_padding_top_size"
android:singleLine="true"
tools:ignore="PrivateResource" />
<ImageView
android:id="@+id/iv_remove_sender"
android:layout_width="18dp"
android:layout_height="18dp"
android:contentDescription="@string/sender_del"
android:src="@drawable/ic_delete"
app:tint="#F15C58" />
</LinearLayout>
</LinearLayout>

View File

@ -0,0 +1,200 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!--放一些组件的样式-->
<style name="WindowAnimStyle" parent="@android:style/Animation">
<item name="android:activityOpenEnterAnimation">@anim/xpage_slide_in_right</item>
<item name="android:activityOpenExitAnimation">@anim/xpage_slide_out_left</item>
<item name="android:activityCloseEnterAnimation">@anim/xpage_slide_in_left</item>
<item name="android:activityCloseExitAnimation">@anim/xpage_slide_out_right</item>
</style>
<style name="Container">
<item name="android:paddingStart">?attr/xui_config_content_spacing_horizontal</item>
<item name="android:paddingEnd">?attr/xui_config_content_spacing_horizontal</item>
</style>
<style name="InfoItem" parent="Container">
<item name="android:layout_width">match_parent</item>
<item name="android:layout_height">50dp</item>
<item name="sLeftViewMarginLeft">10dp</item>
<item name="sRightViewMarginRight">0dp</item>
<item name="sRightIconMarginRight">0dp</item>
<item name="sLeftIconMarginLeft">0dp</item>
<item name="sLeftTextSize">?attr/xui_config_size_content_text</item>
<item name="sLeftTextColor">@color/xui_config_color_title_text</item>
<item name="sCenterTextColor">@color/xui_config_color_black</item>
<item name="sRightTextColor">@color/xui_config_color_explain_text</item>
<item name="sRightTextSize">?attr/xui_config_size_content_text</item>
</style>
<style name="InfoItem.About">
<item name="sLeftViewWidth">110dp</item>
<item name="sLeftViewGravity">left_center</item>
<item name="sCenterViewGravity">left_center</item>
<item name="sCenterViewMarginLeft">25dp</item>
<item name="sRightTextSize">?attr/xui_config_size_content_text</item>
<item name="sRightViewMarginRight">10dp</item>
<item name="sRightIconRes">@drawable/icon_arrow_right_grey</item>
</style>
<style name="InfoItem.AboutWithBtn">
<item name="sLeftViewGravity">left_center</item>
<item name="sCenterViewGravity">left_center</item>
<item name="sCenterViewMarginLeft">25dp</item>
<item name="sRightTextSize">?attr/xui_config_size_content_text</item>
<item name="sRightViewMarginRight">10dp</item>
<!--<item name="sRightIconRes">@drawable/icon_arrow_right_grey</item>-->
</style>
<style name="PullDownStyle">
<item name="android:layout_width">match_parent</item>
<item name="android:layout_height">match_parent</item>
<item name="srlEnableOverScrollDrag">true</item>
<item name="srlEnablePureScrollMode">true</item>
</style>
<style name="ScrollViewStyle">
<item name="android:layout_width">match_parent</item>
<item name="android:layout_height">match_parent</item>
<item name="android:overScrollMode">never</item>
</style>
<style name="XUILayout">
<item name="xui_borderWidth">0dp</item>
<item name="xui_radius">8dp</item>
<item name="xui_shadowElevation">6dp</item>
<item name="android:background">@color/xui_config_color_white</item>
</style>
<!-- SwitchButton -->
<style name="SwitchButtonStyle">
<item name="android:paddingStart">5dp</item>
<item name="android:paddingEnd">5dp</item>
<item name="android:paddingTop">0dp</item>
<item name="android:paddingBottom">0dp</item>
</style>
<style name="BarStyle">
<item name="android:layout_marginTop">5dp</item>
<item name="android:layout_marginStart">5dp</item>
<item name="android:layout_marginEnd">5dp</item>
<item name="android:padding">10dp</item>
<item name="android:background">@drawable/rounded_background</item>
<item name="android:gravity">center_vertical</item>
<!--<item name="android:orientation">horizontal</item>-->
</style>
<style name="BarStyle.Switch">
<item name="android:layout_marginTop">5dp</item>
<item name="android:layout_marginStart">5dp</item>
<item name="android:layout_marginEnd">5dp</item>
<item name="android:paddingStart">10dp</item>
<item name="android:paddingEnd">5dp</item>
<item name="android:paddingTop">10dp</item>
<item name="android:paddingBottom">10dp</item>
<item name="android:background">@drawable/rounded_background</item>
<item name="android:gravity">center_vertical</item>
</style>
<style name="BarStyle.NoMT">
<item name="android:layout_marginStart">5dp</item>
<item name="android:layout_marginEnd">5dp</item>
<item name="android:padding">10dp</item>
<item name="android:background">@drawable/rounded_background</item>
<item name="android:gravity">center_vertical</item>
</style>
<style name="BarTitleStyle">
<item name="android:layout_width">match_parent</item>
<item name="android:layout_height">wrap_content</item>
<item name="android:layout_marginStart">5dp</item>
<item name="android:layout_marginTop">5dp</item>
<item name="android:layout_marginEnd">5dp</item>
<item name="android:gravity">center_vertical</item>
<item name="android:orientation">horizontal</item>
</style>
<style name="BarTitleStyle.Task">
<item name="android:layout_marginTop">20dp</item>
</style>
<style name="insertButtonStyle">
<item name="android:layout_width">0dp</item>
<item name="android:layout_height">wrap_content</item>
<item name="android:paddingTop">3dp</item>
<item name="android:paddingBottom">3dp</item>
<item name="android:textColor">@color/white</item>
<item name="android:background">@color/colorBlueGrey</item>
<item name="android:gravity">center</item>
<item name="android:layout_weight">1</item>
<item name="android:minWidth">0dp</item>
<item name="android:minHeight">0dp</item>
<item name="android:singleLine">true</item>
<item name="android:textSize">@dimen/text_size_small</item>
</style>
<style name="rg_style">
<item name="android:layout_width">match_parent</item>
<item name="android:layout_height">wrap_content</item>
</style>
<style name="rg_rb_style">
<item name="android:layout_width">0dp</item>
<item name="android:layout_height">wrap_content</item>
<item name="android:layout_gravity">center_vertical</item>
<item name="android:layout_weight">1</item>
<item name="android:singleLine">true</item>
<item name="android:padding">0dp</item>
<item name="android:layout_margin">0dp</item>
<item name="android:drawablePadding">5dp</item>S
<item name="android:minHeight">30dp</item>
<item name="android:minWidth">30dp</item>
<item name="android:textSize">@dimen/text_size_small</item>
<item name="android:button">@drawable/custom_radio_button</item>
</style>
<style name="rg_rb_style_wrap">
<item name="android:layout_width">wrap_content</item>
<item name="android:layout_height">wrap_content</item>
<item name="android:layout_gravity">center_vertical</item>
<item name="android:singleLine">true</item>
<item name="android:padding">0dp</item>
<item name="android:drawablePadding">5dp</item>
<item name="android:minHeight">30dp</item>
<item name="android:minWidth">30dp</item>
<item name="android:textSize">@dimen/text_size_small</item>
<item name="android:layout_marginEnd">5dp</item>
<item name="android:button">@drawable/custom_radio_button</item>
</style>
<style name="rg_rb_style_match">
<item name="android:layout_marginTop">5dp</item>
<item name="android:layout_width">match_parent</item>
<item name="android:layout_height">wrap_content</item>
<item name="android:singleLine">true</item>
<!--<item name="android:textSize">@dimen/text_size_small</item>-->
<item name="android:button">@drawable/custom_radio_button</item>
</style>
<style name="TabLayoutStyle" parent="Base.Widget.Design.TabLayout">
<item name="tabIndicatorColor">?attr/colorAccent</item>
<item name="tabIndicatorHeight">2dp</item>
<item name="tabPaddingStart">12dp</item>
<item name="tabPaddingEnd">12dp</item>
<item name="tabTextAppearance">@style/TabText</item>
<item name="tabSelectedTextColor">?attr/colorAccent</item>
<item name="tabTextColor">@color/xui_config_color_gray_6</item>
</style>
<style name="TabText" parent="TextAppearance.Design.Tab">
<item name="android:textColor">@color/selector_tab_text_color</item>
<item name="android:textSize">?attr/xui_config_size_content_text</item>
</style>
<style name="TabText.Explain">
<item name="android:textColor">@color/selector_tab_text_color</item>
<item name="android:textSize">?attr/xui_config_size_explain_text</item>
</style>
</resources>

View File

@ -590,7 +590,7 @@
<string name="timeout">超时</string> <string name="timeout">超时</string>
<string name="timeout_label">单次超时</string> <string name="timeout_label">单次超时</string>
<string name="seconds"></string> <string name="seconds"></string>
<string name="seconds_n">%s </string> <string name="seconds_n">%s秒</string>
<string name="retry">重试</string> <string name="retry">重试</string>
<string name="retry_label">最多重试</string> <string name="retry_label">最多重试</string>
<string name="test_sender_sms">【%s】恭喜您该发送通道测试成功请继续添加转发规则</string> <string name="test_sender_sms">【%s】恭喜您该发送通道测试成功请继续添加转发规则</string>

View File

@ -590,7 +590,7 @@
<string name="timeout">超時</string> <string name="timeout">超時</string>
<string name="timeout_label">單次超時</string> <string name="timeout_label">單次超時</string>
<string name="seconds"></string> <string name="seconds"></string>
<string name="seconds_n">%s </string> <string name="seconds_n">%s秒</string>
<string name="retry">重試</string> <string name="retry">重試</string>
<string name="retry_label">最多重試</string> <string name="retry_label">最多重試</string>
<string name="test_sender_sms">【%s】恭喜您該發送通道測試成功請繼續添加轉發規則</string> <string name="test_sender_sms">【%s】恭喜您該發送通道測試成功請繼續添加轉發規則</string>

View File

@ -590,7 +590,7 @@
<string name="timeout">超时</string> <string name="timeout">超时</string>
<string name="timeout_label">单次超时</string> <string name="timeout_label">单次超时</string>
<string name="seconds"></string> <string name="seconds"></string>
<string name="seconds_n">%s </string> <string name="seconds_n">%s秒</string>
<string name="retry">重试</string> <string name="retry">重试</string>
<string name="retry_label">最多重试</string> <string name="retry_label">最多重试</string>
<string name="test_sender_sms">【%s】恭喜您该发送通道测试成功请继续添加转发规则</string> <string name="test_sender_sms">【%s】恭喜您该发送通道测试成功请继续添加转发规则</string>

View File

@ -105,6 +105,20 @@
<item name="android:gravity">center_vertical</item> <item name="android:gravity">center_vertical</item>
</style> </style>
<style name="BarTitleStyle">
<item name="android:layout_width">match_parent</item>
<item name="android:layout_height">wrap_content</item>
<item name="android:layout_marginStart">5dp</item>
<item name="android:layout_marginTop">10dp</item>
<item name="android:layout_marginEnd">5dp</item>
<item name="android:gravity">bottom</item>
<item name="android:orientation">horizontal</item>
</style>
<style name="BarTitleStyle.Task">
<item name="android:layout_marginTop">20dp</item>
</style>
<style name="insertButtonStyle"> <style name="insertButtonStyle">
<item name="android:layout_width">0dp</item> <item name="android:layout_width">0dp</item>
<item name="android:layout_height">wrap_content</item> <item name="android:layout_height">wrap_content</item>