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

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.RecyclerView
import com.idormy.sms.forwarder.R
import com.idormy.sms.forwarder.adapter.base.ItemMoveCallback
import com.idormy.sms.forwarder.entity.TaskSetting
import java.util.Collections
@Suppress("DEPRECATION")
class TaskSettingAdapter(
val itemList: MutableList<TaskSetting>,
private val editClickListener: (Int) -> Unit,
private val removeClickListener: (Int) -> Unit
var itemList: MutableList<TaskSetting>,
private var removeClickListener: ((Int) -> Unit)? = null,
private var editClickListener: ((Int) -> Unit)? = null,
) : RecyclerView.Adapter<TaskSettingAdapter.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_task_setting_item, parent, false)
val view = LayoutInflater.from(parent.context).inflate(R.layout.adapter_task_setting_item, parent, false)
return ViewHolder(view)
}
@ -48,8 +49,17 @@ class TaskSettingAdapter(
private val dragIcon: ImageView = itemView.findViewById(R.id.iv_drag)
init {
editIcon.setOnClickListener(this)
if (removeClickListener == null) {
removeIcon.visibility = View.GONE
} else {
removeIcon.setOnClickListener(this)
}
if (editClickListener == null) {
editIcon.visibility = View.GONE
} else {
editIcon.setOnClickListener(this)
}
dragIcon.setOnTouchListener { _, event ->
if (event.actionMasked == MotionEvent.ACTION_DOWN) {
@ -69,8 +79,8 @@ class TaskSettingAdapter(
val position = adapterPosition
if (position != RecyclerView.NO_POSITION) {
when (v?.id) {
R.id.iv_edit -> editClickListener(position)
R.id.iv_remove -> removeClickListener(position)
R.id.iv_edit -> editClickListener?.let { it(position) }
R.id.iv_remove -> removeClickListener?.let { it(position) }
}
}
}
@ -79,11 +89,11 @@ class TaskSettingAdapter(
override fun onItemMove(fromPosition: Int, toPosition: Int) {
if (fromPosition < toPosition) {
for (i in fromPosition until toPosition) {
itemList[i] = itemList.set(i + 1, itemList[i])
Collections.swap(itemList, i, i + 1)
}
} else {
for (i in fromPosition downTo toPosition + 1) {
itemList[i] = itemList.set(i - 1, itemList[i])
Collections.swap(itemList, i, i - 1)
}
}
notifyItemMoved(fromPosition, toPosition)
@ -91,40 +101,3 @@ class TaskSettingAdapter(
override fun onDragFinished() {}
}
@Suppress("DEPRECATION")
class ItemMoveCallback(private val listener: Listener) : ItemTouchHelper.Callback() {
interface Listener {
fun onItemMove(fromPosition: Int, toPosition: Int)
fun onDragFinished()
}
override fun getMovementFlags(
recyclerView: RecyclerView,
viewHolder: RecyclerView.ViewHolder
): Int {
val dragFlags = ItemTouchHelper.UP or ItemTouchHelper.DOWN
return makeMovementFlags(dragFlags, 0)
}
override fun onMove(
recyclerView: RecyclerView,
viewHolder: RecyclerView.ViewHolder,
target: RecyclerView.ViewHolder
): Boolean {
listener.onItemMove(viewHolder.adapterPosition, target.adapterPosition)
return true
}
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
// Swiping is not needed for this example
}
override fun onSelectedChanged(viewHolder: RecyclerView.ViewHolder?, actionState: Int) {
super.onSelectedChanged(viewHolder, actionState)
if (actionState == ItemTouchHelper.ACTION_STATE_IDLE) {
listener.onDragFinished()
}
}
}

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 {
holder = convertView.tag as ViewHolder
}
val item = CollectionUtils.getListItem(mDataSource, mIndexs[position]) as FrpcAdapterItem
val item = CollectionUtils.getListItem(mDataSource, mIndexs[position]) as FrpcSpinnerItem
holder.iconView.setImageDrawable(item.icon)
holder.statusView.setImageDrawable(
getDrawable(

View File

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

View File

@ -71,7 +71,7 @@ class RuleSpinnerAdapter<T> : BaseEditSpinnerAdapter<T>, EditSpinnerFilter {
} else {
holder = convertView.tag as ViewHolder
}
val item = CollectionUtils.getListItem(mDataSource, mIndexs[position]) as RuleAdapterItem
val item = CollectionUtils.getListItem(mDataSource, mIndexs[position]) as RuleSpinnerItem
holder.iconView.setImageDrawable(item.icon)
holder.statusView.setImageDrawable(
getDrawable(

View File

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

View File

@ -71,7 +71,7 @@ class SenderSpinnerAdapter<T> : BaseEditSpinnerAdapter<T>, EditSpinnerFilter {
} else {
holder = convertView.tag as ViewHolder
}
val item = CollectionUtils.getListItem(mDataSource, mIndexs[position]) as SenderAdapterItem
val item = CollectionUtils.getListItem(mDataSource, mIndexs[position]) as SenderSpinnerItem
holder.iconView.setImageDrawable(item.icon)
holder.statusView.setImageDrawable(
getDrawable(

View File

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

View File

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

View File

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

View File

@ -8,7 +8,7 @@ class ConvertersSenderList {
@TypeConverter
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

View File

@ -31,6 +31,6 @@ class FrpcRepository(private val frpcDao: FrpcDao) {
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 getByIds(ids: List<Long>) = senderDao.getByIds(ids)
fun getByIds(ids: List<Long>, instr: String) = senderDao.getByIds(ids, instr)
fun getAllNonCache(): List<Sender> {
val query = SimpleSQLiteQuery("SELECT * FROM Sender ORDER BY id ASC")

View File

@ -7,14 +7,19 @@ import android.view.ViewGroup
import android.widget.*
import androidx.fragment.app.viewModels
import androidx.lifecycle.Observer
import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import androidx.work.OneTimeWorkRequestBuilder
import androidx.work.WorkManager
import com.idormy.sms.forwarder.App
import com.idormy.sms.forwarder.R
import com.idormy.sms.forwarder.adapter.SenderRecyclerAdapter
import com.idormy.sms.forwarder.adapter.base.ItemMoveCallback
import com.idormy.sms.forwarder.adapter.spinner.AppListAdapterItem
import com.idormy.sms.forwarder.adapter.spinner.AppListSpinnerAdapter
import com.idormy.sms.forwarder.adapter.spinner.SenderAdapterItem
import com.idormy.sms.forwarder.adapter.spinner.SenderSpinnerAdapter
import com.idormy.sms.forwarder.adapter.spinner.SenderSpinnerItem
import com.idormy.sms.forwarder.core.BaseFragment
import com.idormy.sms.forwarder.core.Core
import com.idormy.sms.forwarder.database.entity.Rule
@ -74,16 +79,17 @@ class RulesEditFragment : BaseFragment<FragmentRulesEditBinding?>(), View.OnClic
private var silentPeriodStart = 0
private var silentPeriodEnd = 0
//当前发送通道
private var senderId = 0L
private var senderListSelected: MutableList<Sender> = mutableListOf()
private var senderItemMap = HashMap<Long, LinearLayout>(2)
//发送通道列表
private var senderListAll: MutableList<Sender> = mutableListOf()
private val senderSpinnerList = ArrayList<SenderAdapterItem>()
//所有发送通道下拉框
private var senderListAll = mutableListOf<Sender>()
private val senderSpinnerList = mutableListOf<SenderSpinnerItem>()
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信息列表
private val appListSpinnerList = ArrayList<AppListAdapterItem>()
private lateinit var appListSpinnerAdapter: AppListSpinnerAdapter<*>
@ -400,49 +406,16 @@ class RulesEditFragment : BaseFragment<FragmentRulesEditBinding?>(), View.OnClic
}
//初始化发送通道下拉框
@SuppressLint("SetTextI18n")
@SuppressLint("SetTextI18n", "NotifyDataSetChanged")
private fun initSenderSpinner() {
//免打扰(禁用转发)时间段
binding!!.tvSilentPeriod.text = mTimeOption[silentPeriodStart] + " ~ " + mTimeOption[silentPeriodEnd]
Core.sender.getAll().subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(object : SingleObserver<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 ->
try {
val sender = senderSpinnerAdapter.getItemSource(position) as SenderAdapterItem
senderId = sender.id!!
val item = senderSpinnerAdapter.getItemSource(position) as SenderSpinnerItem
senderId = item.id!!
if (senderId > 0L) {
senderListSelected.forEach {
if (senderId == it.id) {
@ -453,11 +426,14 @@ class RulesEditFragment : BaseFragment<FragmentRulesEditBinding?>(), View.OnClic
senderListAll.forEach {
if (senderId == it.id) {
senderListSelected.add(it)
addSenderItemLinearLayout(senderItemMap, binding!!.layoutSenders, sender)
}
}
if (STATUS_OFF == sender.status) {
checkSenderLogicShow()
senderRecyclerAdapter.notifyDataSetChanged()
if (STATUS_OFF == item.status) {
XToastUtils.warning(getString(R.string.sender_disabled_tips))
}
}
@ -465,60 +441,80 @@ class RulesEditFragment : BaseFragment<FragmentRulesEditBinding?>(), View.OnClic
XToastUtils.error(e.message.toString())
}
}
// 初始化已选发送通道列表 RecyclerView 和 Adapter
senderRecyclerView = binding!!.recyclerSenders
senderRecyclerAdapter = SenderRecyclerAdapter(senderListSelected, { position ->
senderListSelected.removeAt(position)
senderRecyclerAdapter.notifyItemRemoved(position)
senderRecyclerAdapter.notifyItemRangeChanged(position, senderListSelected.size) // 更新索引
checkSenderLogicShow()
})
senderRecyclerView.apply {
layoutManager = LinearLayoutManager(requireContext())
adapter = senderRecyclerAdapter
}
val senderMoveCallback = ItemMoveCallback(object : ItemMoveCallback.Listener {
override fun onItemMove(fromPosition: Int, toPosition: Int) {
Log.d(TAG, "onItemMove: $fromPosition $toPosition")
senderRecyclerAdapter.onItemMove(fromPosition, toPosition)
senderListSelected = senderRecyclerAdapter.itemList
}
/**
* 动态增删Sender
*
* @param senderItemMap 管理item的map用于删除指定header
* @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)
override fun onDragFinished() {
senderListSelected = senderRecyclerAdapter.itemList
//senderRecyclerAdapter.notifyDataSetChanged()
Log.d(TAG, "onDragFinished: $senderListSelected")
}
})
val senderTouchHelper = ItemTouchHelper(senderMoveCallback)
senderTouchHelper.attachToRecyclerView(senderRecyclerView)
senderRecyclerAdapter.setTouchHelper(senderTouchHelper)
ivSenderImage.setImageDrawable(sender.icon)
ivSenderStatus.setImageDrawable(getDrawable(if (STATUS_OFF == sender.status) R.drawable.ic_stop else R.drawable.ic_start))
val senderItemId = sender.id as Long
tvSenderName.text = "ID-$senderItemId${sender.title}"
//获取发送通道列表
getSenderList()
}
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
//获取发送通道列表
private fun getSenderList() {
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, "getSenderList error: ${e.message}")
}
@SuppressLint("NotifyDataSetChanged")
override fun onSuccess(senderList: List<Sender>) {
if (senderList.isEmpty()) {
XToastUtils.error(R.string.add_sender_first)
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
}
}
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)
}
senderRecyclerAdapter.notifyDataSetChanged()
}
layoutSenders.addView(layoutSenderItem)
senderItemMap[senderItemId] = layoutSenderItem
if (senderListSelected.count() > 1) {
binding!!.layoutSenderLogic.visibility = View.VISIBLE
} else {
binding!!.layoutSenderLogic.visibility = View.GONE
binding!!.rgSenderLogic.check(R.id.rb_sender_logic_all)
}
})
}
//初始化APP下拉列表
@ -581,6 +577,7 @@ class RulesEditFragment : BaseFragment<FragmentRulesEditBinding?>(), View.OnClic
rule.senderList.forEach {
senderId = it.id
senderListSelected.add(it)
Log.d(TAG, it.toString())
}
if (isClone) {
@ -591,6 +588,7 @@ class RulesEditFragment : BaseFragment<FragmentRulesEditBinding?>(), View.OnClic
}
Log.d(TAG, rule.toString())
checkSenderLogicShow()
binding!!.rgSenderLogic.check(rule.getSenderLogicCheckId())
binding!!.rgSimSlot.check(rule.getSimSlotCheckId())
binding!!.rgFiled.check(rule.getFiledCheckId())
@ -619,6 +617,15 @@ class RulesEditFragment : BaseFragment<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 {
if (senderListSelected.isEmpty() || senderId == 0L) {
@ -677,9 +684,7 @@ class RulesEditFragment : BaseFragment<FragmentRulesEditBinding?>(), View.OnClic
else -> CHECK_SIM_SLOT_ALL
}
val status = if (binding!!.sbStatus.isChecked) STATUS_ON else STATUS_OFF
//if (status == STATUS_OFF) {
// throw Exception(getString(R.string.invalid_rule_status))
//}
return Rule(
ruleId,
ruleType,

View File

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

View File

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

View File

@ -6,10 +6,15 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.*
import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.google.gson.Gson
import com.idormy.sms.forwarder.R
import com.idormy.sms.forwarder.adapter.spinner.SenderAdapterItem
import com.idormy.sms.forwarder.adapter.SenderRecyclerAdapter
import com.idormy.sms.forwarder.adapter.base.ItemMoveCallback
import com.idormy.sms.forwarder.adapter.spinner.SenderSpinnerAdapter
import com.idormy.sms.forwarder.adapter.spinner.SenderSpinnerItem
import com.idormy.sms.forwarder.core.BaseFragment
import com.idormy.sms.forwarder.core.Core
import com.idormy.sms.forwarder.database.entity.Rule
@ -52,16 +57,17 @@ class NotificationFragment : BaseFragment<FragmentTasksActionNotificationBinding
private var silentPeriodStart = 0
private var silentPeriodEnd = 0
//当前发送通道
var senderId = 0L
var senderListSelected: MutableList<Sender> = mutableListOf()
private var senderItemMap = HashMap<Long, LinearLayout>(2)
//发送通道列表
var senderListAll: MutableList<Sender> = mutableListOf()
private val senderSpinnerList = ArrayList<SenderAdapterItem>()
//所有发送通道下拉框
private var senderListAll = mutableListOf<Sender>()
private val senderSpinnerList = mutableListOf<SenderSpinnerItem>()
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 ruleType: String = "app"
@ -109,6 +115,7 @@ class NotificationFragment : BaseFragment<FragmentTasksActionNotificationBinding
senderListSelected.add(it)
}
checkSenderLogicShow()
binding!!.rgSenderLogic.check(settingVo.getSenderLogicCheckId())
if (!TextUtils.isEmpty(settingVo.smsTemplate.trim())) {
binding!!.sbSmsTemplate.isChecked = true
@ -293,49 +300,16 @@ class NotificationFragment : BaseFragment<FragmentTasksActionNotificationBinding
}
//初始化发送通道下拉框
@SuppressLint("SetTextI18n")
@SuppressLint("SetTextI18n", "NotifyDataSetChanged")
private fun initSenderSpinner() {
//免打扰(禁用转发)时间段
binding!!.tvSilentPeriod.text = mTimeOption[silentPeriodStart] + " ~ " + mTimeOption[silentPeriodEnd]
Core.sender.getAll().subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(object : SingleObserver<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 ->
try {
val sender = senderSpinnerAdapter.getItemSource(position) as SenderAdapterItem
senderId = sender.id!!
val item = senderSpinnerAdapter.getItemSource(position) as SenderSpinnerItem
senderId = item.id!!
if (senderId > 0L) {
senderListSelected.forEach {
if (senderId == it.id) {
@ -346,11 +320,14 @@ class NotificationFragment : BaseFragment<FragmentTasksActionNotificationBinding
senderListAll.forEach {
if (senderId == it.id) {
senderListSelected.add(it)
addSenderItemLinearLayout(senderItemMap, binding!!.layoutSenders, sender)
}
}
if (STATUS_OFF == sender.status) {
checkSenderLogicShow()
senderRecyclerAdapter.notifyDataSetChanged()
if (STATUS_OFF == item.status) {
XToastUtils.warning(getString(R.string.sender_disabled_tips))
}
}
@ -358,55 +335,84 @@ class NotificationFragment : BaseFragment<FragmentTasksActionNotificationBinding
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
}
/**
* 动态增删Sender
*
* @param senderItemMap 管理item的map用于删除指定header
* @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)
override fun onDragFinished() {
senderListSelected = senderRecyclerAdapter.itemList
//senderRecyclerAdapter.notifyDataSetChanged()
Log.d(TAG, "onDragFinished: $senderListSelected")
}
})
val senderTouchHelper = ItemTouchHelper(senderMoveCallback)
senderTouchHelper.attachToRecyclerView(sendersRecyclerView)
senderRecyclerAdapter.setTouchHelper(senderTouchHelper)
ivSenderImage.setImageDrawable(sender.icon)
ivSenderStatus.setImageDrawable(getDrawable(if (STATUS_OFF == sender.status) R.drawable.ic_stop else R.drawable.ic_start))
val senderItemId = sender.id as Long
tvSenderName.text = "ID-$senderItemId${sender.title}"
//获取发送通道列表
getSenderList()
}
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
//获取发送通道列表
private fun getSenderList() {
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, "getSenderList error: ${e.message}")
}
@SuppressLint("NotifyDataSetChanged")
override fun onSuccess(senderList: List<Sender>) {
if (senderList.isEmpty()) {
XToastUtils.error(R.string.add_sender_first)
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
}
}
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)
}
senderRecyclerAdapter.notifyDataSetChanged()
}
layoutSenders.addView(layoutSenderItem)
senderItemMap[senderItemId] = layoutSenderItem
if (senderListSelected.count() > 1) {
})
}
private fun checkSenderLogicShow() {
if (senderListSelected.size > 1) {
binding!!.layoutSenderLogic.visibility = View.VISIBLE
} else {
binding!!.layoutSenderLogic.visibility = View.GONE

View File

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

View File

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

View File

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

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:maxEms="10"
android:maxLines="1"
android:text="@string/task_cron"
android:textSize="@dimen/text_size_medium"
android:textStyle="bold" />
<ImageView
android:id="@+id/iv_copy"
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_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" />
<TextView
android:id="@+id/tv_time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="@dimen/text_size_small" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="bottom"
android:orientation="horizontal">
<TextView
@ -155,16 +133,38 @@
android:ellipsize="end"
android:gravity="start"
android:maxLines="2"
android:paddingEnd="@dimen/config_padding_10dp"
android:textSize="@dimen/text_size_mini"
tools:ignore="RtlSymmetry" />
android:text="@string/task_cron_tips"
android:textSize="@dimen/text_size_mini" />
<TextView
android:id="@+id/tv_time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:textSize="@dimen/text_size_small" />
<ImageView
android:id="@+id/iv_copy"
android:layout_width="22dp"
android:layout_height="22dp"
android:layout_marginStart="10dp"
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>

View File

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

View File

@ -18,13 +18,8 @@
android:orientation="vertical">
<LinearLayout
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">
style="@style/BarTitleStyle"
android:layout_marginTop="@dimen/config_margin_5dp">
<TextView
android:layout_width="wrap_content"
@ -331,14 +326,7 @@
</LinearLayout>
<LinearLayout
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">
<LinearLayout style="@style/BarTitleStyle">
<TextView
android:layout_width="wrap_content"
@ -873,14 +861,7 @@
</LinearLayout>
<LinearLayout
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">
<LinearLayout style="@style/BarTitleStyle">
<TextView
android:layout_width="wrap_content"
@ -1237,14 +1218,7 @@
</LinearLayout>
<LinearLayout
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">
<LinearLayout style="@style/BarTitleStyle">
<TextView
android:layout_width="wrap_content"

View File

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

View File

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

View File

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

View File

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

View File

@ -28,14 +28,7 @@
app:srcCompat="@drawable/auto_task_icon_settings"
tools:ignore="ImageContrastCheck" />
<LinearLayout
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">
<LinearLayout style="@style/BarTitleStyle">
<TextView
android:layout_width="wrap_content"
@ -344,14 +337,7 @@
</LinearLayout>
<LinearLayout
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">
<LinearLayout style="@style/BarTitleStyle">
<TextView
android:layout_width="wrap_content"

View File

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

View File

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

View File

@ -51,14 +51,7 @@
</LinearLayout>
<LinearLayout
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">
<LinearLayout style="@style/BarTitleStyle.Task">
<TextView
android:layout_width="wrap_content"
@ -69,7 +62,7 @@
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="15dp"
android:layout_marginStart="10dp"
android:text="@string/task_conditions_tips"
android:textSize="@dimen/text_size_mini"
tools:ignore="SmallSp" />
@ -137,14 +130,7 @@
</LinearLayout>
<LinearLayout
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">
<LinearLayout style="@style/BarTitleStyle.Task">
<TextView
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_label">单次超时</string>
<string name="seconds"></string>
<string name="seconds_n">%s </string>
<string name="seconds_n">%s秒</string>
<string name="retry">重试</string>
<string name="retry_label">最多重试</string>
<string name="test_sender_sms">【%s】恭喜您该发送通道测试成功请继续添加转发规则</string>

View File

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

View File

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

View File

@ -105,6 +105,20 @@
<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">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">
<item name="android:layout_width">0dp</item>
<item name="android:layout_height">wrap_content</item>