新增:自动任务·快捷指令

pull/408/head
pppscn 7 months ago
parent eab6c5b049
commit d10d831685

@ -161,14 +161,13 @@ android {
android.applicationVariants.all { variant ->
// Assigns a different version code for each output APK.
variant.outputs.each {
output ->
def date = new Date().format("yyyyMMdd", TimeZone.getTimeZone("GMT+08"))
//noinspection GrDeprecatedAPIUsage
def abiName = output.getFilter(com.android.build.OutputFile.ABI)
if (abiName == null) abiName = "universal"
output.versionCodeOverride = abiCodes.get(abiName, 0) * 100000 + variant.versionCode
output.outputFileName = "SmsF_${versionName}_${output.versionCode}_${abiName}_${date}_${variant.name}.apk"
variant.outputs.each { output ->
def date = new Date().format("yyyyMMdd", TimeZone.getTimeZone("GMT+08"))
//noinspection GrDeprecatedAPIUsage
def abiName = output.getFilter(com.android.build.OutputFile.ABI)
if (abiName == null) abiName = "universal"
output.versionCodeOverride = abiCodes.get(abiName, 0) * 100000 + variant.versionCode
output.outputFileName = "SmsF_${versionName}_${output.versionCode}_${abiName}_${date}_${variant.name}.apk"
}
}
@ -283,6 +282,16 @@ dependencies {
//Location Android LocationManager https://github.com/jenly1314/Location
implementation 'com.github.pppscn:location:1.0.0'
//crontabhttps://github.com/jmrozanec/cron-utils http://cron-parser.com
//implementation 'com.cronutils:cron-utils:9.2.1'
//JSR-310 backport for Androidhttps://github.com/JakeWharton/ThreeTenABP
//implementation 'com.jakewharton.threetenabp:threetenabp:1.4.6'
//Partial implementation of Quartz Cron Java for Android: https://github.com/gatewayapps/crondroid
implementation 'gatewayapps.crondroid:crondroid:1.0.0'
//Java Parser For Cron Expressions: https://github.com/grahamar/cron-parser
implementation 'net.redhogs.cronparser:cron-parser-core:3.5'
}
//X-Library
apply from: 'x-library.gradle'

@ -3,11 +3,11 @@
"Data": [
{
"title": "新用户必读",
"content": "开始设置之前,请您认真地看一遍 <a href=\"https://gitee.com/pp/SmsForwarder/wikis/pages\"><font color=\"#800080\">Wiki</font></a> <br />\n遇到问题请按照 <a href=\"https://gitee.com/pp/SmsForwarder/wikis/pages?sort_id=4877445&doc_id=1821427\"><font color=\"#0000FF\">常见问题</font></a> 章节进行排查!<br />\n没找到答案的再加入QQ互助交流群提问,请清楚地描述问题,并给出对应的配置截图与相关日志,方便大家直观的判断问题! "
"content": "开始设置之前,请您认真地看一遍 <a href=\"https://gitee.com/pp/SmsForwarder/wikis/pages\"><font color=\"#800080\">Wiki</font></a> <br />\n遇到问题请按照 <a href=\"https://gitee.com/pp/SmsForwarder/wikis/pages?sort_id=4877445&doc_id=1821427\"><font color=\"#0000FF\">常见问题</font></a> 章节进行排查!<br />\n没找到答案的再加入互助交流群提问,请清楚地描述问题,并给出对应的配置截图与相关日志,方便大家直观的判断问题! "
},
{
"title": "互助交流群",
"content": "<a href=\"http://qm.qq.com/cgi-bin/qm/qr?k=Mj5m39bqy6eodOImrFLI19Tdeqvv-9zf\">QQ互助交流①群</a><br /><a href=\"http://qm.qq.com/cgi-bin/qm/qr?k=jPXy4YaUzA7Uo0yPPbZXdkb66NS1smU_\">QQ互助交流②群</a><br /><a href=\"https://qm.qq.com/cgi-bin/qm/qr?k=itGVH4lB-HLGyJGTfP_5rjyCQj6kgIBt\">QQ互助交流③群</a><br /><a href=\"https://qm.qq.com/cgi-bin/qm/qr?k=83fYtikg2ARpUECsgJv9CcWTKQB74REK\">QQ互助交流④群</a><br /><a href=\"https://qm.qq.com/cgi-bin/qm/qr?k=CcamLcA-QVN-KqCDjeMZqdTx8IGlJrVx\">QQ互助交流⑤群</a>"
"title": "SmsF 互助交流群",
"content": "<a href=\"https://qun.qq.com/qqweb/qunpro/share?_wv=3&_wwv=128&appChannel=share&inviteCode=1W5aewP&appChannel=share&businessType=9&from=246610&biz=ka\">QQ频道号: q7oofwp13s</a><br /><a href=\"https://t.me/+QBZgnL_fxYM0NjE9\">Telegram 群组</a><br />钉钉群号: 29760014208"
},
{
"title": "打赏名单",

@ -28,6 +28,7 @@ import com.idormy.sms.forwarder.utils.*
import com.idormy.sms.forwarder.utils.sdkinit.UMengInit
import com.idormy.sms.forwarder.utils.sdkinit.XBasicLibInit
import com.idormy.sms.forwarder.utils.sdkinit.XUpdateInit
import com.idormy.sms.forwarder.utils.task.CronUtils
import com.idormy.sms.forwarder.utils.tinker.TinkerLoadLibrary
import io.reactivex.Observable
import io.reactivex.android.schedulers.AndroidSchedulers
@ -206,6 +207,10 @@ class App : Application(), CactusCallback, Configuration.Provider by Core {
XUpdateInit.init(this)
// 运营统计数据
UMengInit.init(this)
// 定时任务初始化
CronUtils.initialize(this)
// 三方时间库初始化
//AndroidThreeTen.init(this)
}
@SuppressLint("CheckResult")

@ -17,7 +17,6 @@ import androidx.recyclerview.widget.RecyclerView
import androidx.viewpager.widget.ViewPager
import com.google.android.material.bottomnavigation.BottomNavigationView
import com.google.android.material.bottomsheet.BottomSheetDialog
import com.gyf.cactus.ext.cactusUpdateNotification
import com.hjq.permissions.OnPermissionCallback
import com.hjq.permissions.Permission
import com.hjq.permissions.XXPermissions
@ -169,6 +168,7 @@ class MainActivity : BaseActivity<ActivityMainBinding?>(),
return@setNavigationItemSelectedListener handleNavigationItemSelected(menuItem)
} else {
when (menuItem.itemId) {
R.id.nav_task -> openNewPage(TasksFragment::class.java)
R.id.nav_server -> openNewPage(ServerFragment::class.java)
R.id.nav_client -> openNewPage(ClientFragment::class.java)
R.id.nav_frpc -> {

@ -0,0 +1,65 @@
package com.idormy.sms.forwarder.adapter
import android.annotation.SuppressLint
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.paging.PagingDataAdapter
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.RecyclerView
import com.idormy.sms.forwarder.R
import com.idormy.sms.forwarder.adapter.TaskPagingAdapter.MyViewHolder
import com.idormy.sms.forwarder.database.entity.Task
import com.idormy.sms.forwarder.databinding.AdapterTasksCardViewListItemBinding
class TaskPagingAdapter(private val itemClickListener: OnItemClickListener) : PagingDataAdapter<Task, MyViewHolder>(diffCallback) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
val binding = AdapterTasksCardViewListItemBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return MyViewHolder(binding)
}
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
val item = getItem(position)
if (item != null) {
holder.binding.ivImage.setImageResource(item.imageId)
holder.binding.ivStatus.setImageResource(item.statusImageId)
holder.binding.tvName.text = item.name
/*holder.binding.cardView.setOnClickListener { view: View? ->
itemClickListener.onItemClicked(view, item)
}*/
holder.binding.ivCopy.setImageResource(R.drawable.ic_copy)
holder.binding.ivEdit.setImageResource(R.drawable.ic_edit)
holder.binding.ivDelete.setImageResource(R.drawable.ic_delete)
holder.binding.ivCopy.setOnClickListener { view: View? ->
itemClickListener.onItemClicked(view, item)
}
holder.binding.ivEdit.setOnClickListener { view: View? ->
itemClickListener.onItemClicked(view, item)
}
holder.binding.ivDelete.setOnClickListener { view: View? ->
itemClickListener.onItemClicked(view, item)
}
}
}
class MyViewHolder(val binding: AdapterTasksCardViewListItemBinding) : RecyclerView.ViewHolder(binding.root)
interface OnItemClickListener {
fun onItemClicked(view: View?, item: Task)
fun onItemRemove(view: View?, id: Int)
}
companion object {
var diffCallback: DiffUtil.ItemCallback<Task> = object : DiffUtil.ItemCallback<Task>() {
override fun areItemsTheSame(oldItem: Task, newItem: Task): Boolean {
return oldItem.id == newItem.id
}
@SuppressLint("DiffUtilEquals")
override fun areContentsTheSame(oldItem: Task, newItem: Task): Boolean {
return oldItem === newItem
}
}
}
}

@ -0,0 +1,92 @@
package com.idormy.sms.forwarder.adapter.spinner
import android.content.Context
import android.graphics.drawable.Drawable
import com.xuexiang.xui.utils.ResUtils
@Suppress("unused")
class ActionAdapterItem {
//标题内容
var title: CharSequence
//图标
var icon: Drawable? = null
//ID
var id: Long? = 0L
//状态
var status: Int? = 1
constructor(title: CharSequence) {
this.title = title
}
constructor(title: CharSequence, icon: Drawable?) {
this.title = title
this.icon = icon
}
constructor(title: CharSequence, icon: Drawable?, id: Long?) {
this.title = title
this.icon = icon
this.id = id
}
constructor(title: CharSequence, icon: Drawable?, id: Long?, status: Int?) {
this.title = title
this.icon = icon
this.id = id
this.status = status
}
constructor(title: CharSequence, drawableId: Int) : this(title, ResUtils.getDrawable(drawableId))
constructor(title: CharSequence, drawableId: Int, id: Long) : this(title, ResUtils.getDrawable(drawableId), id)
constructor(title: CharSequence, drawableId: Int, id: Long, status: Int) : this(title, ResUtils.getDrawable(drawableId), id, status)
constructor(context: Context?, titleId: Int, drawableId: Int) : this(ResUtils.getString(titleId), ResUtils.getDrawable(context, drawableId))
constructor(context: Context?, titleId: Int, drawableId: Int, id: Long) : this(ResUtils.getString(titleId), ResUtils.getDrawable(context, drawableId), id)
constructor(context: Context?, titleId: Int, drawableId: Int, id: Long, status: Int) : this(ResUtils.getString(titleId), ResUtils.getDrawable(context, drawableId), id, status)
constructor(context: Context?, title: CharSequence, drawableId: Int) : this(title, ResUtils.getDrawable(context, drawableId))
constructor(context: Context?, title: CharSequence, drawableId: Int, id: Long) : this(title, ResUtils.getDrawable(context, drawableId), id)
constructor(context: Context?, title: CharSequence, drawableId: Int, id: Long, status: Int) : this(title, ResUtils.getDrawable(context, drawableId), id, status)
fun setStatus(status: Int): ActionAdapterItem {
this.status = status
return this
}
fun setId(id: Long): ActionAdapterItem {
this.id = id
return this
}
fun setTitle(title: CharSequence): ActionAdapterItem {
this.title = title
return this
}
fun setIcon(icon: Drawable?): ActionAdapterItem {
this.icon = icon
return this
}
//注意自定义实体需要重写对象的toString方法
override fun toString(): String {
return title.toString()
}
companion object {
fun of(title: CharSequence): ActionAdapterItem {
return ActionAdapterItem(title)
}
fun arrayof(title: Array<CharSequence>): Array<ActionAdapterItem?> {
val array = arrayOfNulls<ActionAdapterItem>(title.size)
for (i in array.indices) {
array[i] = ActionAdapterItem(title[i])
}
return array
}
}
}

@ -0,0 +1,92 @@
package com.idormy.sms.forwarder.adapter.spinner
import android.content.Context
import android.graphics.drawable.Drawable
import com.xuexiang.xui.utils.ResUtils
@Suppress("unused")
class ConditionAdapterItem {
//标题内容
var title: CharSequence
//图标
var icon: Drawable? = null
//ID
var id: Long? = 0L
//状态
var status: Int? = 1
constructor(title: CharSequence) {
this.title = title
}
constructor(title: CharSequence, icon: Drawable?) {
this.title = title
this.icon = icon
}
constructor(title: CharSequence, icon: Drawable?, id: Long?) {
this.title = title
this.icon = icon
this.id = id
}
constructor(title: CharSequence, icon: Drawable?, id: Long?, status: Int?) {
this.title = title
this.icon = icon
this.id = id
this.status = status
}
constructor(title: CharSequence, drawableId: Int) : this(title, ResUtils.getDrawable(drawableId))
constructor(title: CharSequence, drawableId: Int, id: Long) : this(title, ResUtils.getDrawable(drawableId), id)
constructor(title: CharSequence, drawableId: Int, id: Long, status: Int) : this(title, ResUtils.getDrawable(drawableId), id, status)
constructor(context: Context?, titleId: Int, drawableId: Int) : this(ResUtils.getString(titleId), ResUtils.getDrawable(context, drawableId))
constructor(context: Context?, titleId: Int, drawableId: Int, id: Long) : this(ResUtils.getString(titleId), ResUtils.getDrawable(context, drawableId), id)
constructor(context: Context?, titleId: Int, drawableId: Int, id: Long, status: Int) : this(ResUtils.getString(titleId), ResUtils.getDrawable(context, drawableId), id, status)
constructor(context: Context?, title: CharSequence, drawableId: Int) : this(title, ResUtils.getDrawable(context, drawableId))
constructor(context: Context?, title: CharSequence, drawableId: Int, id: Long) : this(title, ResUtils.getDrawable(context, drawableId), id)
constructor(context: Context?, title: CharSequence, drawableId: Int, id: Long, status: Int) : this(title, ResUtils.getDrawable(context, drawableId), id, status)
fun setStatus(status: Int): ConditionAdapterItem {
this.status = status
return this
}
fun setId(id: Long): ConditionAdapterItem {
this.id = id
return this
}
fun setTitle(title: CharSequence): ConditionAdapterItem {
this.title = title
return this
}
fun setIcon(icon: Drawable?): ConditionAdapterItem {
this.icon = icon
return this
}
//注意自定义实体需要重写对象的toString方法
override fun toString(): String {
return title.toString()
}
companion object {
fun of(title: CharSequence): ConditionAdapterItem {
return ConditionAdapterItem(title)
}
fun arrayof(title: Array<CharSequence>): Array<ConditionAdapterItem?> {
val array = arrayOfNulls<ConditionAdapterItem>(title.size)
for (i in array.indices) {
array[i] = ConditionAdapterItem(title[i])
}
return array
}
}
}

@ -1,14 +1,11 @@
package com.idormy.sms.forwarder.core
import android.app.Application
import android.content.Intent
import android.util.Log
import androidx.core.content.ContextCompat
import androidx.work.Configuration
import com.idormy.sms.forwarder.App
import com.idormy.sms.forwarder.BuildConfig
import com.idormy.sms.forwarder.database.repository.*
import com.idormy.sms.forwarder.service.ForegroundService
import kotlinx.coroutines.launch
@Suppress("unused")
@ -19,6 +16,7 @@ object Core : Configuration.Provider {
val logs: LogsRepository by lazy { (app as App).logsRepository }
val rule: RuleRepository by lazy { (app as App).ruleRepository }
val sender: SenderRepository by lazy { (app as App).senderRepository }
val task: TaskRepository by lazy { (app as App).taskRepository }
fun init(app: Application) {
this.app = app

@ -13,7 +13,7 @@ import com.idormy.sms.forwarder.database.ext.ConvertersDate
import com.idormy.sms.forwarder.utils.DATABASE_NAME
@Database(
entities = [Frpc::class, Msg::class, Logs::class, Rule::class, Sender::class],
entities = [Frpc::class, Msg::class, Logs::class, Rule::class, Sender::class, Task::class],
views = [LogsDetail::class],
version = 18,
exportSchema = false
@ -26,6 +26,7 @@ abstract class AppDatabase : RoomDatabase() {
abstract fun logsDao(): LogsDao
abstract fun ruleDao(): RuleDao
abstract fun senderDao(): SenderDao
abstract fun taskDao(): TaskDao
companion object {
@Volatile
@ -39,11 +40,8 @@ abstract class AppDatabase : RoomDatabase() {
private fun buildDatabase(context: Context): AppDatabase {
val builder = Room.databaseBuilder(
context.applicationContext,
AppDatabase::class.java,
DATABASE_NAME
)
.allowMainThreadQueries() //TODO:允许主线程访问,后面再优化
context.applicationContext, AppDatabase::class.java, DATABASE_NAME
).allowMainThreadQueries() //TODO:允许主线程访问,后面再优化
.addCallback(object : Callback() {
override fun onCreate(db: SupportSQLiteDatabase) {
//fillInDb(context.applicationContext)
@ -80,8 +78,7 @@ custom_domains = smsf.demo.com
""".trimIndent()
)
}
})
.addMigrations(
}).addMigrations(
MIGRATION_1_2,
MIGRATION_2_3,
MIGRATION_3_4,
@ -98,7 +95,7 @@ custom_domains = smsf.demo.com
MIGRATION_14_15,
MIGRATION_15_16,
MIGRATION_16_17,
MIGRATION_17_18
MIGRATION_17_18,
)
/*if (BuildConfig.DEBUG) {
@ -382,10 +379,25 @@ CREATE TABLE "Logs" (
}
}
//规则配置增加uid条件
//自动化任务
private val MIGRATION_17_18 = object : Migration(17, 18) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("Alter table rule add column uid INTEGER NOT NULL DEFAULT 0 ")
database.execSQL(
"""
CREATE TABLE "Task" (
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
"type" INTEGER NOT NULL DEFAULT 1,
"name" TEXT NOT NULL DEFAULT '',
"description" TEXT NOT NULL DEFAULT '',
"conditions" TEXT NOT NULL DEFAULT '',
"actions" TEXT NOT NULL DEFAULT '',
"last_exec_time" INTEGER NOT NULL,
"next_exec_time" INTEGER NOT NULL,
"status" INTEGER NOT NULL DEFAULT 1
)
""".trimIndent()
)
//TODO:原来的电量/网络/SIM卡状态转换为自动化任务
}
}

@ -0,0 +1,52 @@
package com.idormy.sms.forwarder.database.dao
import androidx.paging.PagingSource
import androidx.room.Dao
import androidx.room.Insert
import androidx.room.Query
import androidx.room.RawQuery
import androidx.room.Transaction
import androidx.room.Update
import androidx.sqlite.db.SupportSQLiteQuery
import com.idormy.sms.forwarder.database.entity.Task
import io.reactivex.Single
@Dao
interface TaskDao {
@Query("SELECT * FROM Task where id=:id")
fun get(id: Long): Single<Task>
@Query("SELECT * FROM Task where id=:id")
fun getOne(id: Long): Task
@Query("SELECT * FROM Task ORDER BY id DESC")
fun getAll(): List<Task>
@Query("SELECT * FROM Task where type=:type ORDER BY id DESC")
fun pagingSource(type: String): PagingSource<Int, Task>
@Transaction
@RawQuery(observedEntities = [Task::class])
fun getAllRaw(query: SupportSQLiteQuery): List<Task>
@Query("SELECT * FROM Task WHERE type = :taskType")
fun getByType(taskType: String): List<Task>
//TODO:根据条件查询,不推荐使用
@Query("SELECT * FROM Task WHERE type = :taskType AND conditions LIKE '%' || :conditionKey || '%' AND conditions LIKE '%' || :conditionValue || '%'")
fun getByCondition(taskType: String, conditionKey: String, conditionValue: String): List<Task>
@Insert
fun insert(task: Task)
@Update
fun update(task: Task)
@Query("DELETE FROM Task WHERE id = :taskId")
fun delete(taskId: Long)
@Query("DELETE FROM Task")
fun deleteAll()
}

@ -0,0 +1,69 @@
package com.idormy.sms.forwarder.database.entity
import android.os.Parcelable
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey
import com.idormy.sms.forwarder.R
import com.idormy.sms.forwarder.utils.STATUS_OFF
import com.idormy.sms.forwarder.utils.TYPE_BARK
import com.idormy.sms.forwarder.utils.TYPE_DINGTALK_GROUP_ROBOT
import com.idormy.sms.forwarder.utils.TYPE_DINGTALK_INNER_ROBOT
import com.idormy.sms.forwarder.utils.TYPE_EMAIL
import com.idormy.sms.forwarder.utils.TYPE_FEISHU
import com.idormy.sms.forwarder.utils.TYPE_FEISHU_APP
import com.idormy.sms.forwarder.utils.TYPE_GOTIFY
import com.idormy.sms.forwarder.utils.TYPE_PUSHPLUS
import com.idormy.sms.forwarder.utils.TYPE_SERVERCHAN
import com.idormy.sms.forwarder.utils.TYPE_SMS
import com.idormy.sms.forwarder.utils.TYPE_SOCKET
import com.idormy.sms.forwarder.utils.TYPE_TELEGRAM
import com.idormy.sms.forwarder.utils.TYPE_URL_SCHEME
import com.idormy.sms.forwarder.utils.TYPE_WEBHOOK
import com.idormy.sms.forwarder.utils.TYPE_WEWORK_AGENT
import com.idormy.sms.forwarder.utils.TYPE_WEWORK_ROBOT
import kotlinx.parcelize.Parcelize
import java.util.Date
@Parcelize
@Entity(tableName = "Task")
data class Task(
@PrimaryKey(autoGenerate = true) var id: Long = 0,
@ColumnInfo(name = "type", defaultValue = "1") var type: Int = 1, // 任务类型字段
@ColumnInfo(name = "name", defaultValue = "") val name: String = "", // 任务名称
@ColumnInfo(name = "description", defaultValue = "") val description: String = "", // 任务描述
@ColumnInfo(name = "conditions", defaultValue = "") val conditions: String = "", // 触发条件
@ColumnInfo(name = "actions", defaultValue = "") val actions: String = "", // 执行动作
@ColumnInfo(name = "last_exec_time") var lastExecTime: Date = Date(), // 上次执行时间
@ColumnInfo(name = "next_exec_time") var nextExecTime: Date = Date(), // 下次执行时间
@ColumnInfo(name = "status", defaultValue = "1") var status: Int = 1, // 任务状态
) : Parcelable {
val imageId: Int
get() = when (type) {
TYPE_DINGTALK_GROUP_ROBOT -> R.drawable.icon_dingtalk
TYPE_EMAIL -> R.drawable.icon_email
TYPE_BARK -> R.drawable.icon_bark
TYPE_WEBHOOK -> R.drawable.icon_webhook
TYPE_WEWORK_ROBOT -> R.drawable.icon_wework_robot
TYPE_WEWORK_AGENT -> R.drawable.icon_wework_agent
TYPE_SERVERCHAN -> R.drawable.icon_serverchan
TYPE_TELEGRAM -> R.drawable.icon_telegram
TYPE_FEISHU -> R.drawable.icon_feishu
TYPE_PUSHPLUS -> R.drawable.icon_pushplus
TYPE_GOTIFY -> R.drawable.icon_gotify
TYPE_SMS -> R.drawable.icon_sms
TYPE_DINGTALK_INNER_ROBOT -> R.drawable.icon_dingtalk_inner
TYPE_FEISHU_APP -> R.drawable.icon_feishu_app
TYPE_URL_SCHEME -> R.drawable.icon_url_scheme
TYPE_SOCKET -> R.drawable.icon_socket
else -> R.drawable.icon_sms
}
val statusImageId: Int
get() = when (status) {
STATUS_OFF -> R.drawable.icon_off
else -> R.drawable.icon_on
}
}

@ -0,0 +1,30 @@
package com.idormy.sms.forwarder.database.repository
import androidx.annotation.WorkerThread
import androidx.sqlite.db.SimpleSQLiteQuery
import com.idormy.sms.forwarder.database.dao.TaskDao
import com.idormy.sms.forwarder.database.entity.Task
class TaskRepository(private val taskDao: TaskDao) {
@WorkerThread
fun insert(task: Task) = taskDao.insert(task)
fun getOne(id: Long) = taskDao.getOne(id)
fun update(task: Task) = taskDao.update(task)
fun getAllNonCache(): List<Task> {
val query = SimpleSQLiteQuery("SELECT * FROM Task ORDER BY id ASC")
return taskDao.getAllRaw(query)
}
@WorkerThread
fun delete(id: Long) {
taskDao.delete(id)
}
fun deleteAll() {
taskDao.deleteAll()
}
}

@ -17,26 +17,36 @@ class BaseViewModelFactory(private val context: Context?) : ViewModelProvider.Fa
@Suppress("UNCHECKED_CAST")
return FrpcViewModel(frpcDao) as T
}
modelClass.isAssignableFrom(MsgViewModel::class.java) -> {
val msgDao = AppDatabase.getInstance(context).msgDao()
@Suppress("UNCHECKED_CAST")
return MsgViewModel(msgDao) as T
}
modelClass.isAssignableFrom(LogsViewModel::class.java) -> {
val logDao = AppDatabase.getInstance(context).logsDao()
@Suppress("UNCHECKED_CAST")
return LogsViewModel(logDao) as T
}
modelClass.isAssignableFrom(RuleViewModel::class.java) -> {
val ruleDao = AppDatabase.getInstance(context).ruleDao()
@Suppress("UNCHECKED_CAST")
return RuleViewModel(ruleDao) as T
}
modelClass.isAssignableFrom(SenderViewModel::class.java) -> {
val senderDao = AppDatabase.getInstance(context).senderDao()
@Suppress("UNCHECKED_CAST")
return SenderViewModel(senderDao) as T
}
modelClass.isAssignableFrom(TaskViewModel::class.java) -> {
val taskDao = AppDatabase.getInstance(context).taskDao()
@Suppress("UNCHECKED_CAST")
return TaskViewModel(taskDao) as T
}
}
throw IllegalArgumentException("Unknown ViewModel class")

@ -0,0 +1,39 @@
package com.idormy.sms.forwarder.database.viewmodel
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import androidx.paging.Pager
import androidx.paging.PagingConfig
import androidx.paging.PagingData
import androidx.paging.cachedIn
import com.idormy.sms.forwarder.database.dao.TaskDao
import com.idormy.sms.forwarder.database.entity.Task
import com.idormy.sms.forwarder.database.ext.ioThread
import kotlinx.coroutines.flow.Flow
class TaskViewModel(private val dao: TaskDao) : ViewModel() {
private var type: String = "sms"
fun setType(type: String): TaskViewModel {
this.type = type
return this
}
val allTasks: Flow<PagingData<Task>> = Pager(
config = PagingConfig(
pageSize = 10,
enablePlaceholders = false,
initialLoadSize = 10
)
) {
dao.pagingSource(type)
}.flow.cachedIn(viewModelScope)
fun insertOrUpdate(task: Task) = ioThread {
if (task.id > 0) dao.update(task) else dao.insert(task)
}
fun delete(id: Long) = ioThread {
dao.delete(id)
}
}

@ -4,6 +4,7 @@ import com.google.gson.annotations.SerializedName
import com.idormy.sms.forwarder.database.entity.Frpc
import com.idormy.sms.forwarder.database.entity.Rule
import com.idormy.sms.forwarder.database.entity.Sender
import com.idormy.sms.forwarder.database.entity.Task
import java.io.Serializable
data class CloneInfo(
@ -24,4 +25,7 @@ data class CloneInfo(
@SerializedName("frpc_list")
var frpcList: List<Frpc>? = null,
@SerializedName("task_list")
var taskList: List<Task>? = null,
) : Serializable

@ -0,0 +1,8 @@
package com.idormy.sms.forwarder.entity.task
import java.io.Serializable
data class CronSetting(
var expression: String,
var description: String = "",
) : Serializable

@ -0,0 +1,24 @@
package com.idormy.sms.forwarder.entity.task
import com.idormy.sms.forwarder.R
import com.idormy.sms.forwarder.utils.TYPE_BARK
import com.idormy.sms.forwarder.utils.TYPE_DINGTALK_GROUP_ROBOT
import com.idormy.sms.forwarder.utils.TYPE_EMAIL
import java.io.Serializable
data class TaskSetting(
val type: Int,
val title: String,
val description: String,
var setting: String = "",
var position: Int = -1
) : Serializable {
val iconId: Int
get() = when (type) {
TYPE_DINGTALK_GROUP_ROBOT -> R.drawable.icon_dingtalk
TYPE_EMAIL -> R.drawable.icon_email
TYPE_BARK -> R.drawable.icon_bark
else -> R.drawable.icon_sms
}
}

@ -458,7 +458,7 @@ class RulesEditFragment : BaseFragment<FragmentRulesEditBinding?>(), View.OnClic
}
/**
* 动态增删header
* 动态增删Sender
*
* @param senderItemMap 管理item的map用于删除指定header
* @param layoutSenders 需要挂载item的LinearLayout

@ -0,0 +1,352 @@
package com.idormy.sms.forwarder.fragment
import android.annotation.SuppressLint
import android.content.Intent
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.*
import androidx.fragment.app.viewModels
import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.bottomsheet.BottomSheetDialog
import com.google.gson.Gson
import com.idormy.sms.forwarder.R
import com.idormy.sms.forwarder.adapter.WidgetItemAdapter
import com.idormy.sms.forwarder.adapter.spinner.ActionAdapterItem
import com.idormy.sms.forwarder.adapter.spinner.ConditionAdapterItem
import com.idormy.sms.forwarder.core.BaseFragment
import com.idormy.sms.forwarder.database.AppDatabase
import com.idormy.sms.forwarder.database.entity.Sender
import com.idormy.sms.forwarder.database.entity.Task
import com.idormy.sms.forwarder.database.viewmodel.BaseViewModelFactory
import com.idormy.sms.forwarder.database.viewmodel.TaskViewModel
import com.idormy.sms.forwarder.databinding.FragmentTasksEditBinding
import com.idormy.sms.forwarder.entity.task.CronSetting
import com.idormy.sms.forwarder.entity.task.TaskSetting
import com.idormy.sms.forwarder.utils.*
import com.xuexiang.xaop.annotation.SingleClick
import com.xuexiang.xpage.annotation.Page
import com.xuexiang.xpage.base.XPageFragment
import com.xuexiang.xpage.core.PageOption
import com.xuexiang.xpage.model.PageInfo
import com.xuexiang.xrouter.annotation.AutoWired
import com.xuexiang.xrouter.launcher.XRouter
import com.xuexiang.xui.adapter.recyclerview.RecyclerViewHolder
import com.xuexiang.xui.utils.DensityUtils
import com.xuexiang.xui.utils.WidgetUtils
import com.xuexiang.xui.widget.actionbar.TitleBar
import io.reactivex.SingleObserver
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.Disposable
import io.reactivex.schedulers.Schedulers
import kotlinx.coroutines.*
import java.util.*
@Page(name = "自动任务·编辑器")
@Suppress("PrivatePropertyName", "unused", "DEPRECATION", "UNUSED_PARAMETER")
class TasksEditFragment : BaseFragment<FragmentTasksEditBinding?>(), View.OnClickListener, CompoundButton.OnCheckedChangeListener, RecyclerViewHolder.OnItemClickListener<PageInfo> {
private val TAG: String = TasksEditFragment::class.java.simpleName
private val that = this
var titleBar: TitleBar? = null
private val viewModel by viewModels<TaskViewModel> { BaseViewModelFactory(context) }
private val dialog: BottomSheetDialog by lazy {
BottomSheetDialog(requireContext())
}
//触发条件列表
private var conditionId = 0L
private var conditionListSelected: MutableList<Sender> = mutableListOf()
private var conditionItemMap = HashMap<Long, LinearLayout>(2)
//执行动作列表
private var actionId = 0L
private var actionListSelected: MutableList<Sender> = mutableListOf()
private var actionItemMap = HashMap<Long, LinearLayout>(2)
@JvmField
@AutoWired(name = KEY_RULE_ID)
var taskId: Long = 0
@JvmField
@AutoWired(name = KEY_RULE_TYPE)
var taskType: String = "sms"
@JvmField
@AutoWired(name = KEY_RULE_CLONE)
var isClone: Boolean = false
//初始化数据
private val itemListConditions = mutableListOf(
TaskSetting(TYPE_DINGTALK_GROUP_ROBOT, "Item 1", "Description 1"), TaskSetting(TYPE_EMAIL, "Item 2", "Description 2"), TaskSetting(TYPE_BARK, "Item 3", "Description 3")
// ... other items
)
private val itemListActions = mutableListOf(
TaskSetting(TYPE_DINGTALK_GROUP_ROBOT, "Apple", "Description Apple"), TaskSetting(TYPE_EMAIL, "Banana", "Description Banana"), TaskSetting(TYPE_BARK, "Orange", "Description Orange")
// ... other items
)
override fun initArgs() {
XRouter.getInstance().inject(this)
}
override fun viewBindingInflate(
inflater: LayoutInflater,
container: ViewGroup,
): FragmentTasksEditBinding {
return FragmentTasksEditBinding.inflate(inflater, container, false)
}
override fun initTitle(): TitleBar? {
titleBar = super.initTitle()!!.setImmersive(false)
titleBar!!.setTitle(R.string.menu_tasks)
return titleBar
}
/**
* 初始化控件
*/
override fun initViews() {
if (taskId <= 0) { //新增
titleBar?.setSubTitle(getString(R.string.add_task))
binding!!.btnDel.setText(R.string.discard)
} else { //编辑 & 克隆
binding!!.btnDel.setText(R.string.del)
initForm()
}
}
override fun initListeners() {
binding!!.layoutAddCondition.setOnClickListener(this)
binding!!.layoutAddAction.setOnClickListener(this)
binding!!.btnTest.setOnClickListener(this)
binding!!.btnDel.setOnClickListener(this)
binding!!.btnSave.setOnClickListener(this)
}
override fun onCheckedChanged(buttonView: CompoundButton?, isChecked: Boolean) {/*when (buttonView?.id) {
}*/
}
@SuppressLint("InflateParams")
@SingleClick
override fun onClick(v: View) {
try {
when (v.id) {
R.id.layout_add_condition -> {
val view: View = LayoutInflater.from(requireContext()).inflate(R.layout.dialog_task_condition_bottom_sheet, null)
val recyclerView: RecyclerView = view.findViewById(R.id.recyclerView)
WidgetUtils.initGridRecyclerView(recyclerView, 4, DensityUtils.dp2px(1f))
val widgetItemAdapter = WidgetItemAdapter(TASK_CONDITION_FRAGMENT_LIST)
widgetItemAdapter.setOnItemClickListener(that)
recyclerView.adapter = widgetItemAdapter
dialog.setContentView(view)
dialog.setCancelable(true)
dialog.setCanceledOnTouchOutside(true)
dialog.show()
WidgetUtils.transparentBottomSheetDialogBackground(dialog)
}
R.id.layout_add_action -> {
val view: View = LayoutInflater.from(requireContext()).inflate(R.layout.dialog_task_action_bottom_sheet, null)
val recyclerView: RecyclerView = view.findViewById(R.id.recyclerView)
WidgetUtils.initGridRecyclerView(recyclerView, 4, DensityUtils.dp2px(1f))
val widgetItemAdapter = WidgetItemAdapter(TASK_ACTION_FRAGMENT_LIST)
widgetItemAdapter.setOnItemClickListener(that)
recyclerView.adapter = widgetItemAdapter
dialog.setContentView(view)
dialog.setCancelable(true)
dialog.setCanceledOnTouchOutside(true)
dialog.show()
WidgetUtils.transparentBottomSheetDialogBackground(dialog)
}
R.id.btn_test -> {
val taskNew = checkForm()
testTask(taskNew)
return
}
R.id.btn_del -> {
if (taskId <= 0 || isClone) {
popToBack()
return
}
//TODO: 删除前确认
return
}
R.id.btn_save -> {
val taskNew = checkForm()
if (isClone) taskNew.id = 0
Log.d(TAG, taskNew.toString())
viewModel.insertOrUpdate(taskNew)
XToastUtils.success(R.string.tipSaveSuccess)
popToBack()
return
}
}
} catch (e: Exception) {
XToastUtils.error(e.message.toString())
e.printStackTrace()
}
}
/**
* 动态增删ConditionItem
*
* @param conditionItemMap 管理item的map用于删除指定header
* @param layoutConditions 需要挂载item的LinearLayout
* @param condition ConditionAdapterItem
*/
@SuppressLint("SetTextI18n")
private fun addConditionItemLinearLayout(
conditionItemMap: MutableMap<Long, LinearLayout>, layoutConditions: LinearLayout, condition: ConditionAdapterItem
) {
val layoutConditionItem = View.inflate(requireContext(), R.layout.item_add_condition, null) as LinearLayout
val ivRemoveCondition = layoutConditionItem.findViewById<ImageView>(R.id.iv_remove_condition)
val ivConditionImage = layoutConditionItem.findViewById<ImageView>(R.id.iv_condition_image)
val tvConditionName = layoutConditionItem.findViewById<TextView>(R.id.tv_condition_name)
ivConditionImage.setImageDrawable(condition.icon)
val conditionItemId = condition.id as Long
tvConditionName.text = "ID-$conditionItemId${condition.title}"
ivRemoveCondition.tag = conditionItemId
ivRemoveCondition.setOnClickListener { view2: View ->
val tagId = view2.tag as Long
layoutConditions.removeView(conditionItemMap[tagId])
conditionItemMap.remove(tagId)
}
layoutConditions.addView(layoutConditionItem)
conditionItemMap[conditionItemId] = layoutConditionItem
if (conditionItemMap.isNotEmpty()) {
binding!!.tvAddCondition.text = getString(R.string.add_condition_continue)
binding!!.tvAddConditionTips.visibility = View.GONE
} else {
binding!!.tvAddCondition.text = getString(R.string.add_condition)
binding!!.tvAddConditionTips.visibility = View.VISIBLE
}
}
/**
* 动态增删ActionItem
*
* @param actionItemMap 管理item的map用于删除指定header
* @param layoutActions 需要挂载item的LinearLayout
* @param action ActionAdapterItem
*/
@SuppressLint("SetTextI18n")
private fun addActionItemLinearLayout(
actionItemMap: MutableMap<Long, LinearLayout>, layoutActions: LinearLayout, action: ActionAdapterItem
) {
val layoutActionItem = View.inflate(requireContext(), R.layout.item_add_action, null) as LinearLayout
val ivRemoveAction = layoutActionItem.findViewById<ImageView>(R.id.iv_remove_action)
val ivActionImage = layoutActionItem.findViewById<ImageView>(R.id.iv_action_image)
val tvActionName = layoutActionItem.findViewById<TextView>(R.id.tv_action_name)
ivActionImage.setImageDrawable(action.icon)
val actionItemId = action.id as Long
tvActionName.text = "ID-$actionItemId${action.title}"
ivRemoveAction.tag = actionItemId
ivRemoveAction.setOnClickListener { view2: View ->
val tagId = view2.tag as Long
layoutActions.removeView(actionItemMap[tagId])
actionItemMap.remove(tagId)
}
layoutActions.addView(layoutActionItem)
actionItemMap[actionItemId] = layoutActionItem
if (actionItemMap.isNotEmpty()) {
binding!!.tvAddAction.text = getString(R.string.add_action_continue)
binding!!.tvAddActionTips.visibility = View.GONE
} else {
binding!!.tvAddAction.text = getString(R.string.add_action)
binding!!.tvAddActionTips.visibility = View.VISIBLE
}
}
//初始化表单
private fun initForm() {
AppDatabase.getInstance(requireContext()).taskDao().get(taskId).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(object : SingleObserver<Task> {
override fun onSubscribe(d: Disposable) {}
override fun onError(e: Throwable) {
e.printStackTrace()
}
override fun onSuccess(task: Task) {
Log.d(TAG, task.toString())
if (isClone) {
titleBar?.setSubTitle(getString(R.string.clone_task))
binding!!.btnDel.setText(R.string.discard)
} else {
titleBar?.setSubTitle(getString(R.string.edit_task))
}
binding!!.etName.setText(task.name)
binding!!.sbStatus.isChecked = task.status == STATUS_ON
}
})
}
//提交前检查表单
private fun checkForm(): Task {
if (conditionListSelected.isEmpty() || conditionId == 0L) {
throw Exception(getString(R.string.new_sender_first))
}
if (actionListSelected.isEmpty() || actionId == 0L) {
throw Exception(getString(R.string.new_sender_first))
}
return Task()
}
private fun testTask(task: Task) {
}
@SingleClick
override fun onItemClick(itemView: View, widgetInfo: PageInfo, pos: Int) {
try {
dialog.dismiss()
Log.d(TAG, "onItemClick: $widgetInfo")
@Suppress("UNCHECKED_CAST") PageOption.to(Class.forName(widgetInfo.classPath) as Class<XPageFragment>) //跳转的fragment
.setRequestCode(pos) //请求码,用于返回结果
.open(this)
} catch (e: Exception) {
e.printStackTrace()
XToastUtils.error(e.message.toString())
}
}
override fun onFragmentResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onFragmentResult(requestCode, resultCode, data)
if (data != null) {
val extras = data.extras
var backData: String? = null
if (resultCode == KEY_BACK_CODE_CONDITION) {
backData = extras!!.getString(KEY_BACK_DATA_CONDITION)
if (backData == null) return
when (requestCode) {
0 -> {
val settingVo = Gson().fromJson(backData, CronSetting::class.java)
val condition = ConditionAdapterItem(settingVo.expression) //TODO: 构建列表项目
addConditionItemLinearLayout(conditionItemMap, binding!!.layoutConditions, condition)
}
}
} else if (resultCode == KEY_BACK_CODE_ACTION) {
backData = extras!!.getString(KEY_BACK_DATA_ACTION)
}
Log.d(TAG, "requestCode:$requestCode resultCode:$resultCode backData:$backData")
}
}
}

@ -0,0 +1,129 @@
package com.idormy.sms.forwarder.fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.viewModels
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.RecyclerView.RecycledViewPool
import com.alibaba.android.vlayout.VirtualLayoutManager
import com.idormy.sms.forwarder.R
import com.idormy.sms.forwarder.adapter.TaskPagingAdapter
import com.idormy.sms.forwarder.core.BaseFragment
import com.idormy.sms.forwarder.database.entity.Task
import com.idormy.sms.forwarder.database.viewmodel.BaseViewModelFactory
import com.idormy.sms.forwarder.database.viewmodel.TaskViewModel
import com.idormy.sms.forwarder.databinding.FragmentTasksBinding
import com.idormy.sms.forwarder.utils.EVENT_UPDATE_RULE_TYPE
import com.idormy.sms.forwarder.utils.KEY_RULE_CLONE
import com.idormy.sms.forwarder.utils.KEY_RULE_ID
import com.idormy.sms.forwarder.utils.KEY_RULE_TYPE
import com.idormy.sms.forwarder.utils.XToastUtils
import com.jeremyliao.liveeventbus.LiveEventBus
import com.scwang.smartrefresh.layout.api.RefreshLayout
import com.xuexiang.xaop.annotation.SingleClick
import com.xuexiang.xpage.annotation.Page
import com.xuexiang.xpage.core.PageOption
import com.xuexiang.xui.utils.ResUtils
import com.xuexiang.xui.utils.ThemeUtils
import com.xuexiang.xui.widget.actionbar.TitleBar
import com.xuexiang.xui.widget.dialog.materialdialog.DialogAction
import com.xuexiang.xui.widget.dialog.materialdialog.MaterialDialog
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch
@Suppress("PropertyName", "DEPRECATION")
@Page(name = "自动任务")
class TasksFragment : BaseFragment<FragmentTasksBinding?>(), TaskPagingAdapter.OnItemClickListener {
val TAG: String = TasksFragment::class.java.simpleName
var titleBar: TitleBar? = null
private var adapter = TaskPagingAdapter(this)
private val viewModel by viewModels<TaskViewModel> { BaseViewModelFactory(context) }
private var currentType: String = "mine"
override fun viewBindingInflate(
inflater: LayoutInflater,
container: ViewGroup,
): FragmentTasksBinding {
return FragmentTasksBinding.inflate(inflater, container, false)
}
override fun initTitle(): TitleBar? {
titleBar = super.initTitle()!!.setImmersive(false)
titleBar!!.setTitle(R.string.menu_tasks)
titleBar!!.setActionTextColor(ThemeUtils.resolveColor(context, R.attr.colorAccent))
titleBar!!.addAction(object : TitleBar.ImageAction(R.drawable.ic_add) {
@SingleClick
override fun performAction(view: View) {
openNewPage(TasksEditFragment::class.java)
}
})
return titleBar
}
/**
* 初始化控件
*/
override fun initViews() {
val virtualLayoutManager = VirtualLayoutManager(requireContext())
binding!!.recyclerView.layoutManager = virtualLayoutManager
val viewPool = RecycledViewPool()
binding!!.recyclerView.setRecycledViewPool(viewPool)
viewPool.setMaxRecycledViews(0, 10)
binding!!.tabBar.setTabTitles(ResUtils.getStringArray(R.array.task_type_option))
binding!!.tabBar.setOnTabClickListener { _, position ->
//XToastUtils.toast("点击了$title--$position")
currentType = when (position) {
1 -> "fixed"
else -> "mine"
}
viewModel.setType(currentType)
LiveEventBus.get(EVENT_UPDATE_RULE_TYPE, String::class.java).post(currentType)
adapter.refresh()
binding!!.recyclerView.scrollToPosition(0)
}
}
override fun initListeners() {
binding!!.recyclerView.adapter = adapter
//下拉刷新
binding!!.refreshLayout.setOnRefreshListener { refreshLayout: RefreshLayout ->
refreshLayout.layout.postDelayed({
//adapter!!.refresh()
lifecycleScope.launch {
viewModel.setType(currentType).allTasks.collectLatest { adapter.submitData(it) }
}
refreshLayout.finishRefresh()
}, 200)
}
binding!!.refreshLayout.autoRefresh()
}
override fun onItemClicked(view: View?, item: Task) {
when (view?.id) {
R.id.iv_copy -> {
PageOption.to(TasksEditFragment::class.java).setNewActivity(true).putLong(KEY_RULE_ID, item.id).putString(KEY_RULE_TYPE, item.type.toString()).putBoolean(KEY_RULE_CLONE, true).open(this)
}
R.id.iv_edit -> {
PageOption.to(TasksEditFragment::class.java).setNewActivity(true).putLong(KEY_RULE_ID, item.id).putString(KEY_RULE_TYPE, item.type.toString()).open(this)
}
R.id.iv_delete -> {
MaterialDialog.Builder(requireContext()).title(R.string.delete_task_title).content(R.string.delete_task_tips).positiveText(R.string.lab_yes).negativeText(R.string.lab_no).onPositive { _: MaterialDialog?, _: DialogAction? ->
viewModel.delete(item.id)
XToastUtils.success(R.string.delete_task_toast)
}.show()
}
else -> {}
}
}
override fun onItemRemove(view: View?, id: Int) {}
}

@ -0,0 +1,23 @@
package com.idormy.sms.forwarder.receiver
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.util.Log
import com.idormy.sms.forwarder.database.entity.Task
@Suppress("PropertyName")
class AlarmReceiver : BroadcastReceiver() {
val TAG: String = AlarmReceiver::class.java.simpleName
override fun onReceive(context: Context, intent: Intent) {
val task = intent.getParcelableExtra<Task>("task")
// 根据任务信息执行相应操作
if (task != null) {
Log.d(TAG, "onReceive task $task")
// 处理任务逻辑,例如执行特定操作或者更新界面
}
}
}

@ -476,33 +476,74 @@ var CLIENT_FRAGMENT_LIST = listOf(
)
//自动任务
var TASK_FRAGMENT_LIST = listOf(
const val KEY_TEST_CONDITION = "key_test_condition"
const val KEY_EVENT_DATA_CONDITION = "event_data_condition"
const val KEY_BACK_CODE_CONDITION = 1000
const val KEY_BACK_DATA_CONDITION = "back_data_condition"
const val KEY_TEST_ACTION = "key_test_action"
const val KEY_EVENT_DATA_ACTION = "event_data_action"
const val KEY_BACK_CODE_ACTION = 2000
const val KEY_BACK_DATA_ACTION = "back_data_action"
const val TASK_CRON = 0
var TASK_CONDITION_FRAGMENT_LIST = listOf(
PageInfo(
getString(R.string.dingtalk_robot),
"com.idormy.sms.forwarder.fragment.senders.DingtalkGroupRobotFragment",
getString(R.string.task_cron),
"com.idormy.sms.forwarder.fragment.condition.CronFragment",
"{\"\":\"\"}",
CoreAnim.slide,
R.drawable.icon_dingtalk
R.drawable.auto_task_icon_cron
),
PageInfo(
getString(R.string.email),
"com.idormy.sms.forwarder.fragment.senders.EmailFragment",
"{\"\":\"\"}",
CoreAnim.slide,
R.drawable.icon_email
R.drawable.auto_task_icon_battery
),
PageInfo(
getString(R.string.bark),
"com.idormy.sms.forwarder.fragment.senders.BarkFragment",
"{\"\":\"\"}",
CoreAnim.slide,
R.drawable.icon_bark
R.drawable.auto_task_icon_charge
),
PageInfo(
getString(R.string.webhook),
"com.idormy.sms.forwarder.fragment.senders.WebhookFragment",
"{\"\":\"\"}",
CoreAnim.slide,
R.drawable.icon_webhook
R.drawable.auto_task_icon_wlan
),
)
var TASK_ACTION_FRAGMENT_LIST = listOf(
PageInfo(
getString(R.string.task_cron),
"com.idormy.sms.forwarder.fragment.condition.CronFragment",
"{\"\":\"\"}",
CoreAnim.slide,
R.drawable.auto_task_icon_cron
),
PageInfo(
getString(R.string.email),
"com.idormy.sms.forwarder.fragment.senders.EmailFragment",
"{\"\":\"\"}",
CoreAnim.slide,
R.drawable.auto_task_icon_battery
),
PageInfo(
getString(R.string.bark),
"com.idormy.sms.forwarder.fragment.senders.BarkFragment",
"{\"\":\"\"}",
CoreAnim.slide,
R.drawable.auto_task_icon_charge
),
PageInfo(
getString(R.string.webhook),
"com.idormy.sms.forwarder.fragment.senders.WebhookFragment",
"{\"\":\"\"}",
CoreAnim.slide,
R.drawable.auto_task_icon_wlan
),
)

@ -148,6 +148,7 @@ class HttpServerUtils private constructor() {
cloneInfo.senderList = Core.sender.getAllNonCache()
cloneInfo.ruleList = Core.rule.getAllNonCache()
cloneInfo.frpcList = Core.frpc.getAllNonCache()
cloneInfo.taskList = Core.task.getAllNonCache()
return cloneInfo
}
@ -187,6 +188,13 @@ class HttpServerUtils private constructor() {
Core.frpc.insert(frpc)
}
}
//Task配置
Core.task.deleteAll()
if (!cloneInfo.taskList.isNullOrEmpty()) {
for (task in cloneInfo.taskList!!) {
Core.task.insert(task)
}
}
true
} catch (e: Exception) {
e.printStackTrace()

@ -0,0 +1,66 @@
package com.idormy.sms.forwarder.utils.task
import android.annotation.SuppressLint
import android.app.AlarmManager
import android.app.PendingIntent
import android.content.Context
import android.content.Intent
import com.idormy.sms.forwarder.database.entity.Task
import com.idormy.sms.forwarder.receiver.AlarmReceiver
@Suppress("unused")
class CronUtils {
companion object {
@SuppressLint("StaticFieldLeak")
private lateinit var context: Context
fun initialize(context: Context) {
this.context = context.applicationContext
}
fun updateTaskAndScheduleAlarm(task: Task) {
val oldTask = getOldTask(task.id) // 获取旧的任务信息
cancelAlarm(oldTask) // 取消旧任务的定时器
updateTaskInDatabase(task) // 更新任务信息(例如,更新数据库中的任务信息)
scheduleAlarm(task) // 设置新的定时器
}
private fun cancelAlarm(task: Task?) {
val alarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
val alarmIntent = Intent(context, AlarmReceiver::class.java)
val requestCode = task?.id?.toInt() ?: -1
val pendingIntent = PendingIntent.getBroadcast(context, requestCode, alarmIntent, PendingIntent.FLAG_NO_CREATE or PendingIntent.FLAG_IMMUTABLE)
pendingIntent?.let {
alarmManager.cancel(it)
it.cancel()
}
}
private fun scheduleAlarm(task: Task) {
val alarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
val alarmIntent = Intent(context, AlarmReceiver::class.java)
alarmIntent.putExtra("task", task)
val requestCode = task.id.toInt()
val pendingIntent = PendingIntent.getBroadcast(context, requestCode, alarmIntent, PendingIntent.FLAG_IMMUTABLE)
//val now = Calendar.getInstance()
val nextExecutionTime = task.nextExecTime.time
alarmManager.setExact(
AlarmManager.RTC_WAKEUP, nextExecutionTime, pendingIntent
)
}
private fun getOldTask(taskId: Long): Task {
// 实现获取旧任务信息的逻辑
// 返回旧任务信息Task对象
return Task()
}
private fun updateTaskInDatabase(task: Task) {
// 实现更新数据库中任务信息的逻辑
}
}
}

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="25.0dip"
android:height="25.0dip"
android:autoMirrored="true"
android:viewportWidth="25.0"
android:viewportHeight="25.0">
<path
android:fillColor="@color/app_color_theme_4"
android:pathData="M6.66,0.66L18.66,0.66A6,6 0,0 1,24.66 6.66L24.66,18.66A6,6 0,0 1,18.66 24.66L6.66,24.66A6,6 0,0 1,0.66 18.66L0.66,6.66A6,6 0,0 1,6.66 0.66z" />
<path
android:fillColor="#ffffffff"
android:fillType="evenOdd"
android:pathData="M6.109,7.924C5.306,7.924 4.656,8.575 4.656,9.377V15.189C4.656,15.992 5.306,16.642 6.109,16.642H15.553C16.356,16.642 17.006,15.992 17.006,15.189V9.377C17.006,8.575 16.356,7.924 15.553,7.924H6.109ZM19.367,10.104C18.865,10.104 18.459,10.511 18.459,11.012V13.555C18.459,14.057 18.865,14.463 19.367,14.463C19.868,14.463 20.275,14.057 20.275,13.555V11.012C20.275,10.511 19.868,10.104 19.367,10.104Z" />
</vector>

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="25.0dip"
android:height="25.0dip"
android:autoMirrored="true"
android:viewportWidth="25.0"
android:viewportHeight="25.0">
<path
android:fillColor="@color/colorPrimaryDark"
android:pathData="M6.66,0.66L18.66,0.66A6,6 0,0 1,24.66 6.66L24.66,18.66A6,6 0,0 1,18.66 24.66L6.66,24.66A6,6 0,0 1,0.66 18.66L0.66,6.66A6,6 0,0 1,6.66 0.66z" />
<path
android:fillColor="#ffffffff"
android:fillType="evenOdd"
android:pathData="M13.285,5.328C12.461,4.618 11.189,5.207 11.189,6.298V8.095C11.189,8.1 11.188,8.105 11.188,8.11V10.365L7.469,8.362C7.035,8.11 6.48,8.26 6.23,8.696C5.979,9.133 6.128,9.691 6.562,9.942L10.845,12.284L6.563,14.625C6.128,14.876 5.979,15.434 6.23,15.871C6.48,16.307 7.035,16.457 7.469,16.205L11.188,14.202V18.269C11.188,19.36 12.461,19.949 13.285,19.239L17.651,15.773C18.317,15.2 18.219,14.137 17.459,13.696L14.697,12.283L17.46,10.871C18.219,10.43 18.317,9.367 17.652,8.794L13.285,5.328ZM13.003,16.472C13.003,16.467 13.003,16.462 13.003,16.457V13.523L15.887,14.891L13.003,17.079V16.472ZM15.887,9.676L13.003,11.044V7.488L15.887,9.676Z" />
</vector>

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="25.0dip"
android:height="25.0dip"
android:autoMirrored="true"
android:viewportWidth="25.0"
android:viewportHeight="25.0">
<path
android:fillColor="@color/colorPrimaryDark"
android:pathData="M6.66,0.66L18.66,0.66A6,6 0,0 1,24.66 6.66L24.66,18.66A6,6 0,0 1,18.66 24.66L6.66,24.66A6,6 0,0 1,0.66 18.66L0.66,6.66A6,6 0,0 1,6.66 0.66z" />
<path
android:fillColor="#ffffffff"
android:fillType="evenOdd"
android:pathData="M15.874,11.732C15.332,11.28 14.497,11.655 14.497,12.351V13.23C14.497,13.234 14.496,13.238 14.496,13.241V14.679L12.053,13.401C11.767,13.241 11.403,13.337 11.239,13.615C11.074,13.893 11.172,14.249 11.457,14.409L14.496,16.022V16.047L11.458,17.66C11.172,17.82 11.074,18.176 11.239,18.454C11.403,18.733 11.768,18.828 12.053,18.668L14.496,17.39V19.719C14.496,20.414 15.332,20.79 15.874,20.337L18.742,18.127C19.18,17.762 19.115,17.084 18.616,16.803L17.068,16.035L18.616,15.266C19.115,14.985 19.18,14.307 18.742,13.942L15.874,11.732ZM15.688,18.839C15.689,18.835 15.689,18.831 15.689,18.828V16.719L15.716,16.705L17.583,17.565L15.688,18.96V18.839ZM15.716,15.364L17.583,14.505L15.689,13.109V15.35L15.716,15.364Z" />
<path
android:fillColor="#ffffffff"
android:fillType="evenOdd"
android:pathData="M5.909,6.847C5.909,5.501 7,4.41 8.346,4.41H16.596C17.943,4.41 19.034,5.501 19.034,6.847V9.66C19.034,10.177 18.614,10.597 18.096,10.597C17.579,10.597 17.159,10.177 17.159,9.66V6.847C17.159,6.536 16.907,6.285 16.596,6.285H8.346C8.036,6.285 7.784,6.536 7.784,6.847V18.097C7.784,18.408 8.036,18.66 8.346,18.66H9.659C10.177,18.66 10.596,19.079 10.596,19.597C10.596,20.115 10.177,20.535 9.659,20.535H8.346C7,20.535 5.909,19.443 5.909,18.097V6.847Z" />
</vector>

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="25.0dip"
android:height="25.0dip"
android:autoMirrored="true"
android:viewportWidth="25.0"
android:viewportHeight="25.0">
<path
android:fillColor="@color/app_color_theme_5"
android:pathData="M6.66,0.66L18.66,0.66A6,6 0,0 1,24.66 6.66L24.66,18.66A6,6 0,0 1,18.66 24.66L6.66,24.66A6,6 0,0 1,0.66 18.66L0.66,6.66A6,6 0,0 1,6.66 0.66z" />
<path
android:fillColor="#ffffffff"
android:fillType="evenOdd"
android:pathData="M4.655,9.377C4.655,8.575 5.306,7.924 6.108,7.924H15.552C16.355,7.924 17.006,8.575 17.006,9.377V15.189C17.006,15.992 16.355,16.642 15.552,16.642H6.108C5.306,16.642 4.655,15.992 4.655,15.189V9.377ZM18.459,11.012C18.459,10.511 18.865,10.104 19.367,10.104C19.868,10.104 20.275,10.511 20.275,11.012V13.555C20.275,14.057 19.868,14.463 19.367,14.463C18.865,14.463 18.459,14.057 18.459,13.555V11.012ZM11.768,12.272C11.71,12.272 11.657,12.237 11.635,12.184C11.622,12.153 11.62,12.118 11.629,12.086L12.245,10.031C12.355,9.663 11.892,9.398 11.631,9.68L8.726,12.812C8.51,13.044 8.675,13.422 8.992,13.422H10.551C10.609,13.422 10.662,13.457 10.684,13.511C10.697,13.542 10.699,13.576 10.689,13.608L10.074,15.663C9.964,16.031 10.427,16.296 10.688,16.014L13.593,12.882C13.809,12.65 13.644,12.272 13.327,12.272H11.768Z" />
</vector>

@ -0,0 +1,35 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24.0dip"
android:height="24.0dip"
android:autoMirrored="true"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="@color/app_color_theme_7"
android:pathData="M6,0L18,0A6,6 0,0 1,24 6L24,18A6,6 0,0 1,18 24L6,24A6,6 0,0 1,0 18L0,6A6,6 0,0 1,6 0z" />
<path
android:fillColor="#ffffffff"
android:fillType="evenOdd"
android:pathData="M12,15.319C12.568,15.319 13.09,15.518 13.502,15.85C13.603,15.932 13.619,16.08 13.539,16.182C13.533,16.189 13.528,16.195 13.522,16.201L12.214,17.535C12.097,17.655 11.906,17.656 11.788,17.537C11.788,17.537 11.787,17.536 11.786,17.535L10.479,16.201C10.388,16.109 10.389,15.959 10.48,15.867C10.486,15.861 10.493,15.855 10.499,15.85C10.911,15.518 11.433,15.319 12,15.319Z" />
<path
android:fillColor="#ffffffff"
android:fillType="evenOdd"
android:pathData="M12,12.368C13.264,12.368 14.42,12.83 15.313,13.597C15.33,13.612 15.349,13.629 15.37,13.648L15.37,13.648C15.494,13.759 15.505,13.952 15.395,14.077C15.391,14.081 15.387,14.085 15.384,14.089L14.746,14.739C14.634,14.853 14.455,14.86 14.335,14.755C14.303,14.726 14.274,14.703 14.25,14.683C13.632,14.186 12.851,13.889 12,13.889C11.153,13.889 10.374,14.184 9.758,14.677C9.732,14.698 9.701,14.724 9.666,14.755C9.547,14.86 9.367,14.853 9.255,14.739L8.617,14.089C8.5,13.97 8.501,13.777 8.619,13.659C8.623,13.655 8.627,13.651 8.631,13.648C8.657,13.624 8.681,13.603 8.702,13.585C9.593,12.825 10.744,12.368 12,12.368Z" />
<path
android:fillColor="#ffffffff"
android:fillType="evenOdd"
android:pathData="M12,9.325C14.085,9.325 15.987,10.119 17.426,11.423C17.447,11.442 17.47,11.463 17.496,11.488L17.496,11.488C17.618,11.602 17.625,11.795 17.512,11.918C17.509,11.92 17.507,11.923 17.505,11.925L16.869,12.574C16.755,12.69 16.57,12.695 16.451,12.584C16.419,12.554 16.392,12.529 16.368,12.508C15.202,11.474 13.674,10.846 12,10.846C10.328,10.846 8.801,11.473 7.635,12.506C7.611,12.528 7.582,12.554 7.55,12.584L7.55,12.584C7.431,12.695 7.246,12.69 7.132,12.574L6.496,11.925C6.379,11.806 6.38,11.613 6.498,11.495C6.5,11.493 6.502,11.49 6.505,11.488C6.54,11.455 6.571,11.426 6.598,11.403C8.034,10.11 9.926,9.325 12,9.325Z" />
<path
android:fillColor="#ffffffff"
android:fillType="evenOdd"
android:pathData="M12,6.375C14.842,6.375 17.428,7.481 19.361,9.289C19.375,9.302 19.389,9.316 19.406,9.331L19.406,9.331C19.527,9.446 19.532,9.639 19.418,9.761C19.416,9.763 19.414,9.765 19.413,9.767L18.777,10.415C18.662,10.532 18.476,10.536 18.357,10.423C18.345,10.412 18.334,10.401 18.323,10.392C16.661,8.842 14.44,7.896 12,7.896C9.558,7.896 7.335,8.844 5.672,10.396C5.663,10.404 5.653,10.413 5.643,10.423L5.643,10.423C5.524,10.536 5.338,10.532 5.223,10.415L4.587,9.767C4.47,9.647 4.471,9.455 4.589,9.336C4.591,9.335 4.593,9.333 4.595,9.331C4.609,9.318 4.622,9.305 4.634,9.294C6.567,7.482 9.156,6.375 12,6.375Z" />
<path
android:fillColor="#ffffffff"
android:fillType="evenOdd"
android:pathData="M16.346,16.347L16.346,13.259C16.346,12.958 16.59,12.714 16.891,12.714C17.191,12.714 17.435,12.958 17.435,13.259L17.435,17.254C17.435,17.555 17.191,17.799 16.891,17.799C16.874,17.799 16.858,17.799 16.842,17.797C16.772,17.837 16.681,17.838 16.606,17.783L15.544,17.003C15.261,16.795 15.408,16.347 15.759,16.347L16.346,16.347Z" />
<path
android:fillColor="#ffffffff"
android:fillType="evenOdd"
android:pathData="M19.615,14.166L19.615,17.254C19.615,17.555 19.371,17.799 19.07,17.799C18.769,17.799 18.525,17.555 18.525,17.254L18.525,13.258C18.525,12.958 18.769,12.714 19.07,12.714C19.086,12.714 19.102,12.714 19.118,12.716C19.188,12.676 19.279,12.675 19.354,12.73L20.417,13.51C20.7,13.718 20.553,14.166 20.202,14.166L19.615,14.166Z" />
</vector>

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="25.0dip"
android:height="25.0dip"
android:autoMirrored="true"
android:viewportWidth="25.0"
android:viewportHeight="25.0">
<path
android:fillColor="@color/app_color_theme_6"
android:pathData="M6.66,0.66L18.66,0.66A6,6 0,0 1,24.66 6.66L24.66,18.66A6,6 0,0 1,18.66 24.66L6.66,24.66A6,6 0,0 1,0.66 18.66L0.66,6.66A6,6 0,0 1,6.66 0.66z" />
<path
android:fillColor="#ffffffff"
android:fillType="evenOdd"
android:pathData="M12.66,18.66C15.973,18.66 18.66,15.973 18.66,12.66C18.66,9.346 15.973,6.66 12.66,6.66C9.346,6.66 6.66,9.346 6.66,12.66C6.66,15.973 9.346,18.66 12.66,18.66ZM12.66,20.535C17.009,20.535 20.535,17.009 20.535,12.66C20.535,8.31 17.009,4.785 12.66,4.785C8.31,4.785 4.785,8.31 4.785,12.66C4.785,17.009 8.31,20.535 12.66,20.535ZM11.535,10.222C11.535,9.704 11.954,9.285 12.472,9.285C12.99,9.285 13.41,9.704 13.41,10.222V11.91H15.097C15.615,11.91 16.035,12.329 16.035,12.847C16.035,13.365 15.615,13.785 15.097,13.785H12.472C12.44,13.785 12.408,13.783 12.376,13.78C11.903,13.732 11.535,13.333 11.535,12.847V10.222Z" />
</vector>

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24.0dip"
android:height="24.0dip"
android:autoMirrored="true"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="@color/colorPrimaryDark"
android:pathData="M6,0L18,0A6,6 0,0 1,24 6L24,18A6,6 0,0 1,18 24L6,24A6,6 0,0 1,0 18L0,6A6,6 0,0 1,6 0z" />
<path
android:fillColor="#ffffffff"
android:fillType="evenOdd"
android:pathData="M15.214,11.072C14.673,10.62 13.837,10.995 13.837,11.691V12.571C13.837,12.575 13.837,12.578 13.837,12.582V14.02L11.393,12.742C11.108,12.581 10.743,12.677 10.579,12.955C10.415,13.234 10.513,13.589 10.798,13.75L13.837,15.363V15.388L10.798,17C10.513,17.161 10.415,17.516 10.579,17.795C10.744,18.073 11.108,18.169 11.393,18.008L13.837,16.73V19.059C13.837,19.755 14.673,20.13 15.214,19.678L18.083,17.468C18.52,17.102 18.455,16.424 17.957,16.144L16.408,15.375L17.957,14.606C18.456,14.326 18.52,13.648 18.083,13.282L15.214,11.072ZM15.029,18.179C15.029,18.176 15.029,18.172 15.029,18.168V16.06L15.057,16.046L16.923,16.905L15.029,18.301V18.179ZM15.057,14.704L16.923,13.845L15.029,12.45V14.69L15.057,14.704Z" />
<path
android:fillColor="#ffffffff"
android:fillType="evenOdd"
android:pathData="M5.249,6.188C5.249,4.841 6.341,3.75 7.687,3.75H15.937C17.283,3.75 18.374,4.841 18.374,6.188V9C18.374,9.518 17.955,9.938 17.437,9.938C16.919,9.938 16.499,9.518 16.499,9V6.188C16.499,5.877 16.247,5.625 15.937,5.625H7.687C7.376,5.625 7.124,5.877 7.124,6.188V17.438C7.124,17.748 7.376,18 7.687,18H8.999C9.517,18 9.937,18.42 9.937,18.938C9.937,19.455 9.517,19.875 8.999,19.875H7.687C6.341,19.875 5.249,18.784 5.249,17.438V6.188Z" />
<path
android:fillColor="@color/colorPrimaryDark"
android:pathData="M12.389,17.5l6.203,-3.243l0.463,0.886l-6.203,3.243z" />
</vector>

@ -0,0 +1,39 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24.0dip"
android:height="24.0dip"
android:autoMirrored="true"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="@color/app_color_theme_7"
android:pathData="M6,0L18,0A6,6 0,0 1,24 6L24,18A6,6 0,0 1,18 24L6,24A6,6 0,0 1,0 18L0,6A6,6 0,0 1,6 0z" />
<path
android:fillColor="#ffffffff"
android:fillType="evenOdd"
android:pathData="M12,15.319C12.568,15.319 13.09,15.518 13.502,15.85C13.603,15.932 13.619,16.08 13.539,16.182C13.533,16.189 13.528,16.195 13.522,16.201L12.214,17.535C12.097,17.655 11.906,17.656 11.788,17.537C11.788,17.537 11.787,17.536 11.786,17.535L10.479,16.201C10.388,16.109 10.389,15.959 10.48,15.867C10.486,15.861 10.493,15.855 10.499,15.85C10.911,15.518 11.433,15.319 12,15.319Z" />
<path
android:fillColor="#ffffffff"
android:fillType="evenOdd"
android:pathData="M12,12.368C13.264,12.368 14.42,12.83 15.313,13.597C15.33,13.612 15.349,13.629 15.37,13.648L15.37,13.648C15.494,13.759 15.505,13.952 15.395,14.077C15.391,14.081 15.387,14.085 15.384,14.089L14.746,14.739C14.634,14.853 14.455,14.86 14.335,14.755C14.303,14.726 14.274,14.703 14.25,14.683C13.632,14.186 12.851,13.889 12,13.889C11.153,13.889 10.374,14.184 9.758,14.677C9.732,14.698 9.701,14.724 9.666,14.755C9.547,14.86 9.367,14.853 9.255,14.739L8.617,14.089C8.5,13.97 8.501,13.777 8.619,13.659C8.623,13.655 8.627,13.651 8.631,13.648C8.657,13.624 8.681,13.603 8.702,13.585C9.593,12.825 10.744,12.368 12,12.368Z" />
<path
android:fillColor="#ffffffff"
android:fillType="evenOdd"
android:pathData="M12,9.325C14.085,9.325 15.987,10.119 17.425,11.423C17.446,11.442 17.469,11.463 17.495,11.488L17.495,11.488C17.617,11.602 17.624,11.795 17.511,11.918C17.509,11.92 17.507,11.923 17.504,11.925L16.868,12.574C16.754,12.69 16.569,12.695 16.45,12.584C16.419,12.554 16.391,12.529 16.367,12.508C15.201,11.474 13.673,10.846 12,10.846C10.328,10.846 8.8,11.473 7.635,12.506C7.61,12.528 7.582,12.554 7.549,12.584L7.549,12.584C7.43,12.695 7.245,12.69 7.131,12.574L6.495,11.925C6.378,11.806 6.379,11.613 6.497,11.495C6.499,11.493 6.502,11.49 6.504,11.488C6.539,11.455 6.57,11.426 6.597,11.403C8.033,10.11 9.926,9.325 12,9.325Z" />
<path
android:fillColor="#ffffffff"
android:fillType="evenOdd"
android:pathData="M12,6.375C14.842,6.375 17.428,7.481 19.361,9.289C19.375,9.302 19.389,9.316 19.406,9.331L19.406,9.331C19.527,9.446 19.532,9.639 19.418,9.761C19.416,9.763 19.414,9.765 19.413,9.767L18.777,10.415C18.662,10.532 18.476,10.536 18.357,10.423C18.345,10.412 18.334,10.401 18.323,10.392C16.661,8.842 14.44,7.896 12,7.896C9.558,7.896 7.335,8.844 5.672,10.396C5.663,10.404 5.653,10.413 5.643,10.423L5.643,10.423C5.524,10.536 5.338,10.532 5.223,10.415L4.587,9.767C4.47,9.647 4.471,9.455 4.589,9.336C4.591,9.335 4.593,9.333 4.595,9.331C4.609,9.318 4.622,9.305 4.634,9.294C6.567,7.482 9.156,6.375 12,6.375Z" />
<path
android:fillColor="#00000000"
android:pathData="M7,15.973L16.825,6"
android:strokeWidth="1.6"
android:strokeColor="#ffffffff"
android:strokeLineCap="round" />
<path
android:fillColor="#00000000"
android:pathData="M8.14,17.096L17.965,7.123"
android:strokeWidth="1.6"
android:strokeColor="@color/app_color_theme_7"
android:strokeLineCap="round" />
</vector>

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="25.0dip"
android:height="25.0dip"
android:autoMirrored="true"
android:viewportWidth="25.0"
android:viewportHeight="25.0">
<path
android:fillColor="@color/toast_success_color"
android:pathData="M6.66,0.66L18.66,0.66A6,6 0,0 1,24.66 6.66L24.66,18.66A6,6 0,0 1,18.66 24.66L6.66,24.66A6,6 0,0 1,0.66 18.66L0.66,6.66A6,6 0,0 1,6.66 0.66z" />
<path
android:fillColor="#ffffffff"
android:fillType="evenOdd"
android:pathData="M9.372,6.643C9.337,6.583 9.292,6.531 9.238,6.488C9.078,6.322 8.846,6.252 8.622,6.298L8.486,6.314C8.453,6.313 8.427,6.318 8.412,6.321C8.375,6.328 8.344,6.34 8.326,6.348C8.285,6.365 8.24,6.388 8.197,6.412C8.107,6.462 7.991,6.534 7.864,6.615C7.609,6.779 7.295,6.995 7.034,7.186L7.029,7.19L7.025,7.193C6.433,7.657 6.249,8.29 6.29,8.968C6.327,9.585 6.549,10.26 6.823,10.93L6.81,10.951L6.872,11.093C7.346,12.181 8.27,13.582 9.741,15.108L9.738,15.112L9.926,15.3L9.993,15.366L10.009,15.381L10.02,15.392L10.113,15.299L10.114,15.3L10.021,15.394C11.622,16.972 13.094,17.953 14.226,18.447L14.368,18.509L14.389,18.496C15.058,18.77 15.734,18.992 16.351,19.029C17.028,19.07 17.662,18.886 18.126,18.294L18.129,18.29L18.132,18.285C18.323,18.024 18.539,17.71 18.703,17.455C18.785,17.328 18.857,17.212 18.906,17.122C18.931,17.079 18.954,17.034 18.971,16.993C18.979,16.975 18.991,16.944 18.998,16.907C19.001,16.892 19.006,16.866 19.005,16.833L19.02,16.697C19.066,16.473 18.996,16.241 18.831,16.081C18.788,16.028 18.736,15.982 18.676,15.948L18.64,15.92L15.363,14.127L13.785,15.497C13.005,15.085 12.239,14.414 11.574,13.762C10.916,13.093 10.237,12.321 9.822,11.533L11.192,9.955L9.399,6.679L9.372,6.643ZM10.039,15.186L10.039,15.185L10.048,15.176L10.049,15.177C10.059,15.207 10.073,15.237 10.091,15.267L10.09,15.268C10.071,15.244 10.053,15.217 10.039,15.186ZM10.005,14.98L10.02,14.991C10.021,14.98 10.022,14.971 10.024,14.965C10.023,14.965 10.023,14.964 10.022,14.963C10.017,14.968 10.011,14.974 10.005,14.98ZM10.091,15.269L10.092,15.268L10.101,15.281L10.091,15.269ZM10.116,15.298L10.115,15.297L10.234,15.178L10.116,15.298ZM10.431,15.407L10.441,15.398L10.44,15.4L10.431,15.407ZM10.425,15.413L10.428,15.41L10.427,15.411L10.425,15.413ZM10.394,15.435L10.391,15.436L10.403,15.429L10.394,15.435ZM10.408,15.426L10.407,15.427L10.41,15.424L10.408,15.426Z" />
</vector>

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="25.0dip"
android:height="25.0dip"
android:autoMirrored="true"
android:viewportWidth="25.0"
android:viewportHeight="25.0">
<path
android:fillColor="@color/app_color_theme_8"
android:pathData="M6.66,0.66L18.66,0.66A6,6 0,0 1,24.66 6.66L24.66,18.66A6,6 0,0 1,18.66 24.66L6.66,24.66A6,6 0,0 1,0.66 18.66L0.66,6.66A6,6 0,0 1,6.66 0.66z" />
<path
android:fillColor="#ffffffff"
android:fillType="evenOdd"
android:pathData="M13.602,7.035C12.261,7.035 11.029,7.488 10.047,8.25C9.638,8.567 9.049,8.493 8.731,8.084C8.414,7.675 8.488,7.086 8.898,6.769C10.197,5.76 11.831,5.16 13.602,5.16C17.845,5.16 21.285,8.602 21.285,12.847C21.285,17.092 17.845,20.535 13.602,20.535C11.831,20.535 10.197,19.934 8.898,18.926C8.488,18.608 8.414,18.019 8.731,17.61C9.049,17.201 9.638,17.127 10.047,17.444C11.029,18.206 12.261,18.66 13.602,18.66C16.809,18.66 19.41,16.058 19.41,12.847C19.41,9.636 16.809,7.035 13.602,7.035Z" />
<path
android:fillColor="#ffffffff"
android:fillType="evenOdd"
android:pathData="M6.697,15.066C6.919,15.317 7.332,15.16 7.332,14.826L7.332,13.737L10.468,13.737C10.668,13.737 10.831,13.574 10.831,13.373L10.831,11.194C10.831,10.993 10.668,10.831 10.468,10.831L7.332,10.831L7.332,9.561C7.332,9.225 6.916,9.069 6.696,9.322L4.382,11.972C4.261,12.11 4.262,12.315 4.383,12.452L6.697,15.066ZM14.1,13.373C14.1,13.574 13.937,13.737 13.737,13.737L12.647,13.737C12.446,13.737 12.284,13.574 12.284,13.373L12.284,11.194C12.284,10.993 12.446,10.831 12.647,10.831L13.737,10.831C13.937,10.831 14.1,10.993 14.1,11.194L14.1,13.373Z" />
</vector>

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="25.0dip"
android:height="25.0dip"
android:autoMirrored="true"
android:viewportWidth="25.0"
android:viewportHeight="25.0">
<path
android:fillColor="@color/app_color_theme_8"
android:pathData="M6.66,0.66L18.66,0.66A6,6 0,0 1,24.66 6.66L24.66,18.66A6,6 0,0 1,18.66 24.66L6.66,24.66A6,6 0,0 1,0.66 18.66L0.66,6.66A6,6 0,0 1,6.66 0.66z" />
<path
android:fillColor="#ffffffff"
android:pathData="M18.026,7.954C18.161,7.656 18.095,7.309 17.859,7.077C17.622,6.844 17.263,6.771 16.949,6.893L6.265,11.05C5.927,11.181 5.718,11.508 5.748,11.856C5.777,12.205 6.04,12.495 6.395,12.572L10.967,13.562L11.91,17.825C11.986,18.165 12.281,18.42 12.642,18.455C13.002,18.49 13.345,18.297 13.489,17.978L18.026,7.954Z" />
</vector>

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="25.0dip"
android:height="25.0dip"
android:autoMirrored="true"
android:viewportWidth="25.0"
android:viewportHeight="25.0">
<path
android:fillColor="@color/colorPrimaryDark"
android:pathData="M6.66,0.66L18.66,0.66A6,6 0,0 1,24.66 6.66L24.66,18.66A6,6 0,0 1,18.66 24.66L6.66,24.66A6,6 0,0 1,0.66 18.66L0.66,6.66A6,6 0,0 1,6.66 0.66z" />
<path
android:fillColor="#ffffffff"
android:fillType="evenOdd"
android:pathData="M19.99,4c0,-1.1 -0.89,-2 -1.99,-2h-8L4,8v12c0,1.1 0.9,2 2,2h12.01c1.1,0 1.99,-0.9 1.99,-2l-0.01,-16zM9,19L7,19v-2h2v2zM17,19h-2v-2h2v2zM9,15L7,15v-4h2v4zM13,19h-2v-4h2v4zM13,13h-2v-2h2v2zM17,15h-2v-4h2v4z" />
</vector>

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="25.0dip"
android:height="25.0dip"
android:autoMirrored="true"
android:viewportWidth="25.0"
android:viewportHeight="25.0">
<path
android:fillColor="@color/app_color_theme_8"
android:pathData="M6.66,0.66L18.66,0.66A6,6 0,0 1,24.66 6.66L24.66,18.66A6,6 0,0 1,18.66 24.66L6.66,24.66A6,6 0,0 1,0.66 18.66L0.66,6.66A6,6 0,0 1,6.66 0.66z" />
<path
android:fillColor="#ffffffff"
android:fillType="evenOdd"
android:pathData="M13.226,7.035C11.886,7.035 10.654,7.488 9.672,8.25C9.263,8.567 8.674,8.493 8.356,8.084C8.039,7.675 8.113,7.086 8.522,6.769C9.822,5.76 11.455,5.16 13.226,5.16C17.47,5.16 20.91,8.602 20.91,12.847C20.91,17.092 17.47,20.535 13.226,20.535C11.455,20.535 9.822,19.934 8.522,18.926C8.113,18.608 8.039,18.019 8.356,17.61C8.674,17.201 9.263,17.127 9.672,17.444C10.654,18.206 11.886,18.66 13.226,18.66C16.434,18.66 19.035,16.058 19.035,12.847C19.035,9.636 16.434,7.035 13.226,7.035Z" />
<path
android:fillColor="#ffffffff"
android:fillType="evenOdd"
android:pathData="M11.792,9.367C11.57,9.117 11.157,9.274 11.157,9.608V10.831L7.924,10.831C7.723,10.831 7.561,10.993 7.561,11.194L7.561,13.373C7.561,13.574 7.723,13.737 7.924,13.737L11.157,13.737V14.873C11.157,15.209 11.573,15.365 11.794,15.112L14.108,12.461C14.228,12.324 14.227,12.118 14.106,11.982L11.792,9.367ZM4.292,11.193C4.292,10.993 4.454,10.83 4.655,10.83L5.745,10.83C5.945,10.83 6.108,10.993 6.108,11.193L6.108,13.373C6.108,13.573 5.945,13.736 5.745,13.736L4.655,13.736C4.454,13.736 4.292,13.573 4.292,13.373L4.292,11.193Z" />
</vector>

@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="25.0dip"
android:height="25.0dip"
android:autoMirrored="true"
android:viewportWidth="25.0"
android:viewportHeight="25.0">
<path
android:fillColor="@color/colorPrimaryDark"
android:pathData="M6.66,0.66L18.66,0.66A6,6 0,0 1,24.66 6.66L24.66,18.66A6,6 0,0 1,18.66 24.66L6.66,24.66A6,6 0,0 1,0.66 18.66L0.66,6.66A6,6 0,0 1,6.66 0.66z" />
<path
android:fillColor="#ffffffff"
android:fillType="evenOdd"
android:pathData="M12.66,15.979C13.228,15.979 13.75,16.178 14.161,16.51C14.262,16.591 14.279,16.74 14.198,16.842C14.193,16.848 14.188,16.854 14.182,16.861L12.874,18.195C12.757,18.314 12.566,18.315 12.448,18.197C12.447,18.196 12.447,18.195 12.446,18.195L11.138,16.861C11.047,16.768 11.048,16.618 11.14,16.526C11.146,16.52 11.152,16.515 11.159,16.509C11.57,16.177 12.092,15.979 12.66,15.979Z" />
<path
android:fillColor="#ffffffff"
android:fillType="evenOdd"
android:pathData="M12.66,13.027C13.923,13.027 15.08,13.49 15.972,14.257C15.989,14.271 16.009,14.288 16.03,14.307L16.03,14.307C16.154,14.419 16.165,14.611 16.054,14.737C16.051,14.741 16.047,14.745 16.043,14.748L15.406,15.399C15.294,15.513 15.114,15.519 14.994,15.414C14.962,15.386 14.934,15.362 14.909,15.342C14.292,14.845 13.51,14.549 12.66,14.549C11.813,14.549 11.034,14.843 10.418,15.337C10.391,15.358 10.361,15.384 10.326,15.414C10.206,15.52 10.026,15.513 9.914,15.399L9.277,14.749C9.16,14.629 9.161,14.437 9.279,14.318C9.283,14.315 9.286,14.311 9.29,14.307C9.317,14.283 9.341,14.263 9.361,14.245C10.252,13.485 11.403,13.027 12.66,13.027Z" />
<path
android:fillColor="#ffffffff"
android:fillType="evenOdd"
android:pathData="M12.659,9.985C14.744,9.985 16.646,10.778 18.085,12.083C18.105,12.101 18.129,12.123 18.155,12.148L18.155,12.148C18.277,12.262 18.284,12.454 18.171,12.578C18.168,12.58 18.166,12.582 18.164,12.585L17.528,13.234C17.413,13.35 17.229,13.354 17.11,13.243C17.078,13.214 17.051,13.189 17.027,13.168C15.861,12.133 14.333,11.506 12.659,11.506C10.987,11.506 9.46,12.132 8.294,13.165C8.27,13.187 8.241,13.213 8.209,13.243L8.209,13.243C8.09,13.354 7.905,13.35 7.791,13.234L7.155,12.585C7.038,12.465 7.039,12.273 7.157,12.154C7.159,12.152 7.161,12.15 7.164,12.148C7.199,12.114 7.23,12.086 7.256,12.062C8.693,10.77 10.585,9.985 12.659,9.985Z" />
<path
android:fillColor="#ffffffff"
android:fillType="evenOdd"
android:pathData="M12.66,7.035C15.502,7.035 18.088,8.14 20.021,9.949C20.034,9.961 20.049,9.975 20.065,9.991L20.065,9.991C20.186,10.106 20.192,10.299 20.077,10.421C20.076,10.423 20.074,10.425 20.072,10.426L19.436,11.075C19.322,11.192 19.136,11.195 19.017,11.083C19.005,11.071 18.993,11.061 18.983,11.051C17.32,9.502 15.099,8.556 12.66,8.556C10.218,8.556 7.995,9.504 6.332,11.055C6.323,11.064 6.313,11.073 6.302,11.083L6.302,11.083C6.183,11.195 5.997,11.192 5.883,11.075L5.247,10.426C5.13,10.307 5.131,10.114 5.249,9.996C5.25,9.994 5.252,9.992 5.254,9.991C5.268,9.977 5.281,9.965 5.293,9.954C7.227,8.142 9.815,7.035 12.66,7.035Z" />
</vector>

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FFFFFFFF"
android:pathData="M9,3L5,6.99h3L8,14h2L10,6.99h3L9,3zM16,17.01L16,10h-2v7.01h-3L15,21l4,-3.99h-3z" />
</vector>

@ -1,10 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="#333333"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FF000000"
android:fillColor="#FFFFFFFF"
android:pathData="M3,10h11v2H3V10zM3,8h11V6H3V8zM3,16h7v-2H3V16zM18.01,12.87l0.71,-0.71c0.39,-0.39 1.02,-0.39 1.41,0l0.71,0.71c0.39,0.39 0.39,1.02 0,1.41l-0.71,0.71L18.01,12.87zM17.3,13.58l-5.3,5.3V21h2.12l5.3,-5.3L17.3,13.58z" />
</vector>

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="?attr/colorAccent"
android:pathData="M14,2H6C4.9,2 4.01,2.9 4.01,4L4,20c0,1.1 0.89,2 1.99,2H18c1.1,0 2,-0.9 2,-2V8L14,2zM10.94,18L7.4,14.46l1.41,-1.41l2.12,2.12l4.24,-4.24l1.41,1.41L10.94,18zM13,9V3.5L18.5,9H13z" />
</vector>

@ -1,10 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:width="40.0dip"
android:height="50.0dip"
android:tint="?attr/colorControlNormal"
android:viewportWidth="24"
android:viewportHeight="24">
android:viewportWidth="40.0"
android:viewportHeight="50.0">
<path
android:fillColor="@android:color/white"
android:pathData="M19.99,4c0,-1.1 -0.89,-2 -1.99,-2h-8L4,8v12c0,1.1 0.9,2 2,2h12.01c1.1,0 1.99,-0.9 1.99,-2l-0.01,-16zM9,19L7,19v-2h2v2zM17,19h-2v-2h2v2zM9,15L7,15v-4h2v4zM13,19h-2v-4h2v4zM13,13h-2v-2h2v2zM17,15h-2v-4h2v4z" />
android:fillAlpha="0.8"
android:fillColor="#ffffffff"
android:fillType="evenOdd"
android:pathData="M24.6702,0C26.9147,0 29.0561,0.9429 30.5715,2.5987L37.9014,10.6071C39.2513,12.0821 40,14.009 40,16.0084V44C40,47.3137 37.3137,50 34,50H6C2.6863,50 0,47.3137 0,44V6C0,2.6863 2.6863,0 6,0H24.6702Z
M18,43L14,43v-4h4v4z
M34,43h-4v-4h4v4z
M18,35L14,35v-8h4v8z
M26,43h-4v-8h4v8z
M26,31h-4v-4h4v4z
M34,35h-4v-8h4v8z"
android:strokeAlpha="0.8" />
</vector>

@ -1,10 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:width="40.0dip"
android:height="50.0dip"
android:tint="?attr/colorControlNormal"
android:viewportWidth="24"
android:viewportHeight="24">
android:viewportWidth="40.0"
android:viewportHeight="50.0">
<path
android:fillColor="@android:color/white"
android:pathData="M3,5L1,5v16c0,1.1 0.9,2 2,2h16v-2L3,21L3,5zM14,15h2L16,5h-4v2h2v8zM21,1L7,1c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2L23,3c0,-1.1 -0.9,-2 -2,-2zM21,17L7,17L7,3h14v14z" />
android:fillAlpha="0.8"
android:fillColor="#ffffffff"
android:fillType="evenOdd"
android:pathData="M24.6702,0C26.9147,0 29.0561,0.9429 30.5715,2.5987L37.9014,10.6071C39.2513,12.0821 40,14.009 40,16.0084V44C40,47.3137 37.3137,50 34,50H6C2.6863,50 0,47.3137 0,44V6C0,2.6863 2.6863,0 6,0H24.6702ZM22.785,13.8333H18.8717L13.33,15.4483V20.3117L17.7633,19.045V38H22.785V13.8333Z"
android:strokeAlpha="0.8" />
</vector>

@ -1,10 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:width="40.0dip"
android:height="50.0dip"
android:tint="?attr/colorControlNormal"
android:viewportWidth="24"
android:viewportHeight="24">
android:viewportWidth="40.0"
android:viewportHeight="50.0">
<path
android:fillColor="@android:color/white"
android:pathData="M3,5L1,5v16c0,1.1 0.9,2 2,2h16v-2L3,21L3,5zM21,1L7,1c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2L23,3c0,-1.1 -0.9,-2 -2,-2zM21,17L7,17L7,3h14v14zM17,13h-4v-2h2c1.1,0 2,-0.89 2,-2L17,7c0,-1.11 -0.9,-2 -2,-2h-4v2h4v2h-2c-1.1,0 -2,0.89 -2,2v4h6v-2z" />
android:fillAlpha="0.8"
android:fillColor="#ffffffff"
android:fillType="evenOdd"
android:pathData="M30.5715,2.5987C29.0561,0.9429 26.9147,0 24.6702,0H6C2.6863,0 0,2.6863 0,6V44C0,47.3137 2.6863,50 6,50H34C37.3137,50 40,47.3137 40,44V16.0084C40,14.009 39.2513,12.0821 37.9014,10.6071L30.5715,2.5987ZM20.1471,13C17.7983,13 15.7604,13.7514 14.3071,15.1044C12.8518,16.4593 12,18.4019 12,20.7467V20.9918H16.7129V20.7467C16.7129,19.6857 17.0933,18.8354 17.7036,18.2496C18.3155,17.6623 19.1728,17.3274 20.1471,17.3274C22.0709,17.3274 23.3606,18.4956 23.3606,20.0899C23.3606,21.317 22.9263,22.2977 21.5103,23.9022L21.5087,23.9041L12.3677,34.4825V38H27.7794V33.7763H18.6271L24.6667,26.89C26.6563,24.638 28,22.6766 28,20.0899C28,16.0219 24.5918,13 20.1471,13Z"
android:strokeAlpha="0.8" />
</vector>

@ -0,0 +1,86 @@
<?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"
android:paddingStart="5dp"
android:paddingEnd="5dp"
tools:ignore="UseCompoundDrawables">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal">
<ImageView
android:id="@+id/iv_icon"
android:layout_width="36dp"
android:layout_height="36dp"
android:src="@drawable/ic_add"
tools:ignore="ContentDescription,ImageContrastCheck" />
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:layout_weight="1"
android:orientation="vertical">
<TextView
android:id="@+id/tv_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/add_action"
android:textSize="18sp"
android:textStyle="bold" />
<TextView
android:id="@+id/tv_description"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/add_action_tips"
android:textSize="12sp" />
</LinearLayout>
<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:src="@drawable/ic_edit"
app:tint="@color/toast_info_color"
tools:ignore="ContentDescription,PrivateResource,ImageContrastCheck" />
<ImageView
android:id="@+id/iv_remove"
android:layout_width="@dimen/card_view_image_size"
android:layout_height="@dimen/card_view_image_size"
android:padding="@dimen/card_view_image_padding"
android:src="@drawable/ic_delete"
app:tint="@color/toast_error_color"
tools:ignore="ContentDescription,PrivateResource" />
<ImageView
android:id="@+id/iv_drag"
android:layout_width="@dimen/card_view_image_size"
android:layout_height="@dimen/card_view_image_size"
android:padding="@dimen/card_view_image_padding"
android:src="@drawable/ic_drag"
app:tint="@color/colorStart"
tools:ignore="ContentDescription,ImageContrastCheck" />
</LinearLayout>
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_marginTop="5dp"
android:layout_marginBottom="5dp"
android:background="?attr/xui_config_color_separator_light" />
</LinearLayout>

@ -0,0 +1,82 @@
<?xml version="1.0" encoding="utf-8"?>
<com.xuexiang.xui.widget.layout.XUIFrameLayout 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:id="@+id/card_view"
style="@style/XUILayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/config_margin_5dp"
android:layout_marginTop="@dimen/config_margin_5dp"
android:layout_marginEnd="@dimen/config_margin_5dp"
android:paddingStart="@dimen/config_padding_5dp"
android:paddingTop="@dimen/config_padding_5dp"
android:paddingEnd="@dimen/config_padding_5dp"
android:paddingBottom="@dimen/config_padding_5dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="5dp"
android:orientation="vertical">
<ImageView
android:id="@+id/iv_image"
android:layout_width="@dimen/card_view_image_size"
android:layout_height="@dimen/card_view_image_size"
tools:ignore="ContentDescription" />
<ImageView
android:id="@+id/iv_status"
android:layout_width="16dp"
android:layout_height="16dp"
android:layout_marginStart="24dp"
android:layout_marginTop="-16dp"
tools:ignore="ContentDescription" />
</LinearLayout>
<TextView
android:id="@+id/tv_name"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginStart="10dp"
android:layout_weight="1"
android:ellipsize="end"
android:gravity="start"
android:maxEms="8"
android:maxLines="2" />
<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"
app:tint="@color/colorStart"
tools:ignore="ContentDescription" />
<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"
app:tint="@color/toast_info_color"
tools:ignore="ContentDescription,PrivateResource" />
<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"
app:tint="@color/toast_error_color"
tools:ignore="ContentDescription,PrivateResource" />
</LinearLayout>
</com.xuexiang.xui.widget.layout.XUIFrameLayout>

@ -0,0 +1,28 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/bg_bottom_sheet"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:padding="10dp"
android:text="@string/select_task_action" />
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="?attr/xui_config_color_separator_light" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:overScrollMode="never"
tools:listitem="@android:layout/simple_list_item_2" />
</LinearLayout>

@ -0,0 +1,28 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/bg_bottom_sheet"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:padding="10dp"
android:text="@string/select_task_condition" />
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="?attr/xui_config_color_separator_light" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:overScrollMode="never"
tools:listitem="@android:layout/simple_list_item_2" />
</LinearLayout>

@ -0,0 +1,48 @@
<?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="?attr/xui_config_color_background"
android:orientation="vertical"
tools:ignore="Overdraw">
<com.xuexiang.xui.widget.tabbar.EasyIndicator
android:id="@+id/tabBar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
app:indicator_height="42dp"
app:indicator_line_height="2dp"
app:indicator_line_show="true"
app:indicator_textSize="14sp"
app:indicator_width="0dp" />
<com.scwang.smartrefresh.layout.SmartRefreshLayout
android:id="@+id/refreshLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:srlEnableAutoLoadMore="true"
app:srlEnableLoadMore="false">
<com.scwang.smartrefresh.header.MaterialHeader
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:overScrollMode="never"
android:paddingBottom="@dimen/config_padding_5dp"
tools:listitem="@layout/adapter_tasks_card_view_list_item" />
<!-- TODO:注意修改包名时,这里也需要修改 -->
<com.idormy.sms.forwarder.widget.MaterialFooter
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</com.scwang.smartrefresh.layout.SmartRefreshLayout>
</LinearLayout>

File diff suppressed because it is too large Load Diff

@ -0,0 +1,265 @@
<?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="?attr/xui_config_color_background"
android:orientation="vertical">
<androidx.core.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:overScrollMode="never">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="5dp"
android:orientation="vertical">
<LinearLayout
style="@style/taskBarStyleWithSwitch"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/task_name_status"
android:textStyle="bold" />
<com.xuexiang.xui.widget.edittext.materialedittext.MaterialEditText
android:id="@+id/et_name"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="5dp"
android:layout_weight="1"
android:singleLine="true"
app:met_clearButton="true"
tools:ignore="VisualLintTextFieldSize,TouchTargetSizeCheck,SpeakableTextPresentCheck" />
<com.xuexiang.xui.widget.button.switchbutton.SwitchButton
android:id="@+id/sb_status"
style="@style/SwitchButtonStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:checked="true"
tools:ignore="TouchTargetSizeCheck" />
</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="bottom"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/task_conditions"
android:textSize="20sp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="15dp"
android:text="@string/task_conditions_tips"
android:textSize="10sp"
tools:ignore="SmallSp" />
</LinearLayout>
<LinearLayout
style="@style/taskBarStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<LinearLayout
android:id="@+id/layout_conditions"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recycler_conditions"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<LinearLayout
android:id="@+id/layout_add_condition"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal">
<ImageView
android:layout_width="36dp"
android:layout_height="36dp"
android:src="@drawable/ic_add"
app:tint="@color/gray_text_light"
tools:ignore="ContentDescription,ImageContrastCheck" />
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:layout_weight="1"
android:orientation="vertical">
<TextView
android:id="@+id/tv_add_condition"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/add_condition"
android:textSize="18sp"
android:textStyle="bold" />
<TextView
android:id="@+id/tv_add_condition_tips"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/add_condition_tips"
android:textSize="12sp" />
</LinearLayout>
</LinearLayout>
</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="bottom"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/task_actions"
android:textSize="20sp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:text="@string/task_actions_tips"
android:textSize="10sp"
tools:ignore="SmallSp" />
</LinearLayout>
<LinearLayout
style="@style/taskBarStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<LinearLayout
android:id="@+id/layout_actions"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recycler_actions"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<LinearLayout
android:id="@+id/layout_add_action"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal">
<ImageView
android:layout_width="36dp"
android:layout_height="36dp"
android:src="@drawable/ic_add"
app:tint="@color/gray_text_light"
tools:ignore="ContentDescription,ImageContrastCheck" />
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:layout_weight="1"
android:orientation="vertical">
<TextView
android:id="@+id/tv_add_action"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/add_action"
android:textSize="18sp"
android:textStyle="bold" />
<TextView
android:id="@+id/tv_add_action_tips"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/add_action_tips"
android:textSize="12sp" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
</LinearLayout>
</androidx.core.widget.NestedScrollView>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:orientation="horizontal"
android:padding="10dp">
<com.xuexiang.xui.widget.textview.supertextview.SuperButton
android:id="@+id/btn_del"
style="@style/SuperButton.Gray.Icon"
android:drawableStart="@drawable/icon_delete"
android:paddingStart="15dp"
android:text="@string/del"
android:textColor="#FFFFFF"
android:textSize="11sp"
tools:ignore="RtlSymmetry,TextContrastCheck,TouchTargetSizeCheck" />
<com.xuexiang.xui.widget.textview.supertextview.SuperButton
android:id="@+id/btn_save"
style="@style/SuperButton.Blue.Icon"
android:layout_marginStart="10dp"
android:drawableStart="@drawable/icon_save"
android:paddingStart="15dp"
android:text="@string/save"
android:textColor="#FFFFFF"
android:textSize="11sp"
tools:ignore="RtlSymmetry,TextContrastCheck,TouchTargetSizeCheck" />
<com.xuexiang.xui.widget.textview.supertextview.SuperButton
android:id="@+id/btn_test"
style="@style/SuperButton.Green.Icon"
android:layout_marginStart="10dp"
android:drawableStart="@drawable/icon_test"
android:paddingStart="15dp"
android:text="@string/test"
android:textColor="#FFFFFF"
android:textSize="11sp"
tools:ignore="RtlSymmetry,TextContrastCheck,TouchTargetSizeCheck" />
</LinearLayout>
</LinearLayout>

@ -0,0 +1,68 @@
<?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"
android:paddingStart="5dp"
android:paddingEnd="5dp"
tools:ignore="UseCompoundDrawables">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal">
<ImageView
android:id="@+id/iv_action_image"
android:layout_width="36dp"
android:layout_height="36dp"
android:src="@drawable/ic_add"
app:tint="@color/gray_text_light"
tools:ignore="ContentDescription,ImageContrastCheck" />
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:layout_weight="1"
android:orientation="vertical">
<TextView
android:id="@+id/tv_action_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/add_action"
android:textSize="18sp"
android:textStyle="bold" />
<TextView
android:id="@+id/tv_action_tips"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/add_action_tips"
android:textSize="12sp" />
</LinearLayout>
<ImageView
android:id="@+id/iv_remove_action"
android:layout_width="24dp"
android:layout_height="24dp"
android:src="@drawable/icon_delete"
app:tint="@color/gray_text_light"
tools:ignore="ContentDescription,ImageContrastCheck" />
</LinearLayout>
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_marginTop="5dp"
android:layout_marginBottom="5dp"
android:background="?attr/xui_config_color_separator_light" />
</LinearLayout>

@ -0,0 +1,68 @@
<?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"
android:paddingStart="5dp"
android:paddingEnd="5dp"
tools:ignore="UseCompoundDrawables">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal">
<ImageView
android:id="@+id/iv_condition_image"
android:layout_width="36dp"
android:layout_height="36dp"
android:src="@drawable/ic_add"
app:tint="@color/gray_text_light"
tools:ignore="ContentDescription,ImageContrastCheck" />
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:layout_weight="1"
android:orientation="vertical">
<TextView
android:id="@+id/tv_condition_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/add_condition"
android:textSize="18sp"
android:textStyle="bold" />
<TextView
android:id="@+id/tv_condition_tips"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/add_condition_tips"
android:textSize="12sp" />
</LinearLayout>
<ImageView
android:id="@+id/iv_remove_condition"
android:layout_width="24dp"
android:layout_height="24dp"
android:src="@drawable/icon_delete"
app:tint="@color/gray_text_light"
tools:ignore="ContentDescription,ImageContrastCheck" />
</LinearLayout>
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_marginTop="5dp"
android:layout_marginBottom="5dp"
android:background="?attr/xui_config_color_separator_light" />
</LinearLayout>

@ -31,6 +31,12 @@
android:id="@+id/other"
android:checkableBehavior="single">
<item
android:id="@+id/nav_task"
android:checkable="false"
android:icon="@drawable/ic_menu_task"
android:title="@string/menu_tasks" />
<item
android:id="@+id/nav_server"
android:checkable="false"

@ -80,6 +80,12 @@
<item>System App</item>
</string-array>
<!--任务类型-->
<string-array name="task_type_option">
<item>My Task</item>
<item>Task Center</item>
</string-array>
<string-array name="MailType">
<item>\@qq.com</item>
<item>\@foxmail.com</item>

@ -10,7 +10,7 @@
<string name="menu_rules">Rules</string>
<string name="menu_settings">Settings</string>
<string name="menu_task">Task</string>
<string name="menu_tasks">Task</string>
<string name="menu_server">Server</string>
<string name="menu_client">Client</string>
<string name="menu_frpc">Frpc</string>
@ -1030,7 +1030,6 @@
<string name="user_id">User ID</string>
<string name="auto_clean_logs">Auto delete logs N days ago</string>
<string name="auto_clean_logs_tips">0=disabled, scan when battery change</string>
<string name="day">Day</string>
<string name="safety_measures">Safety Measures</string>
<string name="safety_measures_tips">The client and server must be the same. It is strongly recommended to enable encryption when accessing the public network.</string>
<string name="safety_measures_none">None</string>
@ -1079,5 +1078,85 @@
<string name="enable_location_tag_tips">Insert location info into forwarded msg.</string>
<string name="uid">UID</string>
<string name="select_task_type">Please select task type</string>
<string name="task_name_status">Name/Status</string>
<string name="task_name">Task Name</string>
<string name="task_description">Description</string>
<string name="task_conditions">If</string>
<string name="task_conditions_tips">Influenced by the first condition, the others serve as determinants.</string>
<string name="task_actions">then execute.</string>
<string name="task_actions_tips">Allow multiple execution actions, with each execution result being independent of the others.</string>
<string name="task_last_exec_time">Last Exec Time</string>
<string name="task_next_exec_time">Next Exec Time</string>
<string name="add_task">Add Task</string>
<string name="edit_task">Edit Task</string>
<string name="clone_task">Clone Task</string>
<string name="delete_task_title">Delete confirmation</string>
<string name="delete_task_tips">Are you sure to delete this task?</string>
<string name="delete_task_toast">The task has deleted.</string>
<string name="add_condition">添加条件</string>
<string name="add_condition_tips">例如如果电量低于20%时</string>
<string name="add_condition_continue">继续添加条件</string>
<string name="add_action">添加动作</string>
<string name="add_action_tips">例如:禁用所有转发通道</string>
<string name="add_action_continue">继续添加动作</string>
<string name="select_task_condition">Please select condition</string>
<string name="select_task_action">Please select action</string>
<string name="task_cron">Cron</string>
<string name="second">Second</string>
<string name="minute">Minute</string>
<string name="hour">Hour</string>
<string name="day">Day</string>
<string name="month">Month</string>
<string name="week">Week</string>
<string name="year">Year</string>
<string name="cron_expression">Cron Expression</string>
<string name="cron_expression_tips">Quartz Cron Expression</string>
<string name="every_second">Every Second</string>
<string name="every_minute">Every Minute</string>
<string name="every_hour">Every Hour</string>
<string name="every_day">Every Day</string>
<string name="every_month">Every Month</string>
<string name="every_week">Every Week</string>
<string name="every_year">Every Year</string>
<string name="cyclic">Cyclic</string>
<string name="cyclic_from">From</string>
<string name="cyclic_from_week">From week</string>
<string name="cyclic_to">To</string>
<string name="start">Start</string>
<string name="end">End</string>
<string name="interval_seconds_1">Starting from</string>
<string name="interval_seconds_2">second, execute every</string>
<string name="interval_seconds_3">seconds.</string>
<string name="interval_minutes_1">Starting from</string>
<string name="interval_minutes_2">minute, execute every</string>
<string name="interval_minutes_3">minutes.</string>
<string name="interval_hours_1">Starting from</string>
<string name="interval_hours_2">hour, execute every</string>
<string name="interval_hours_3">hours.</string>
<string name="interval_days_1">Starting from</string>
<string name="interval_days_2">day, execute every</string>
<string name="interval_days_3">days.</string>
<string name="interval_months_1">Starting from</string>
<string name="interval_months_2">month, execute every</string>
<string name="interval_months_3">months.</string>
<string name="interval_years_1">Starting from</string>
<string name="interval_years_2">year, execute every</string>
<string name="interval_years_3">years.</string>
<string name="assigned">Assigned</string>
<string name="not_assigned">Not Assigned</string>
<string name="recent_day">Recent Days</string>
<string name="recent_day_1">The nearest working day to the</string>
<string name="recent_day_2">day of each month.</string>
<string name="last_day_of_month">Last day of month</string>
<string name="last_day_of_month_recent_day">Last day of month recent days</string>
<string name="weeks_of_week_1"></string>
<string name="weeks_of_week_2">周的星期</string>
<string name="last_week_of_month">The last [day of the week] of this month.</string>
<string name="last_week_of_month_1">The last week</string>
<string name="last_week_of_month_2">of this month.</string>
<string name="cron_expression_check">Cron Expression Test Result</string>
<string name="invalid_cron_expression">Cron expression is invalid:\n%s</string>
<string name="next_execution_times">The next %s execution times:\n%s</string>
</resources>

@ -80,6 +80,12 @@
<item>系统应用</item>
</string-array>
<!--任务类型-->
<string-array name="task_type_option">
<item>我的任务</item>
<item>任务中心</item>
</string-array>
<string-array name="MailType">
<item>\@qq.com</item>
<item>\@foxmail.com</item>

@ -10,7 +10,7 @@
<string name="menu_rules">转发规则</string>
<string name="menu_settings">通用设置</string>
<string name="menu_task">自动任务·快捷指令</string>
<string name="menu_tasks">自动任务·快捷指令</string>
<string name="menu_server">主动控制·服务端</string>
<string name="menu_client">主动控制·客户端</string>
<string name="menu_frpc">内网穿透·Frpc</string>
@ -1031,7 +1031,6 @@
<string name="user_id">User ID</string>
<string name="auto_clean_logs">自动删除N天前的转发记录</string>
<string name="auto_clean_logs_tips">0=禁用,触发机制:每次电量变化时扫描</string>
<string name="day"></string>
<string name="safety_measures">安全措施</string>
<string name="safety_measures_tips">客户端与服务端必须一致,强烈建议公网访问时启用加密</string>
<string name="safety_measures_none">不需要</string>
@ -1080,5 +1079,85 @@
<string name="enable_location_tag_tips">在转发信息中插入手机的当前定位信息</string>
<string name="uid">UID</string>
<string name="select_task_type">请选择自动任务类型</string>
<string name="task_name_status">任务名称/状态</string>
<string name="task_name">任务名称</string>
<string name="task_description">任务描述</string>
<string name="task_conditions">如果</string>
<string name="task_conditions_tips">由第一个条件触发,其他条件作为判断</string>
<string name="task_actions">就执行</string>
<string name="task_actions_tips">允许添加多个执行动作,执行结果互不干扰</string>
<string name="task_last_exec_time">上次执行时间</string>
<string name="task_next_exec_time">下次执行时间</string>
<string name="add_task">新建任务</string>
<string name="edit_task">编辑任务</string>
<string name="clone_task">克隆任务</string>
<string name="delete_task_title">删除任务操作确认</string>
<string name="delete_task_tips">删除任务操作确认</string>
<string name="delete_task_toast">删除任务操作确认</string>
<string name="add_condition">添加条件</string>
<string name="add_condition_tips">例如如果电量低于20%时</string>
<string name="add_condition_continue">继续添加条件</string>
<string name="add_action">添加动作</string>
<string name="add_action_tips">例如:禁用所有转发通道</string>
<string name="add_action_continue">继续添加动作</string>
<string name="select_task_condition">请选择条件</string>
<string name="select_task_action">请选择动作</string>
<string name="task_cron">定时任务</string>
<string name="second"></string>
<string name="minute"></string>
<string name="hour"></string>
<string name="day"></string>
<string name="month"></string>
<string name="week"></string>
<string name="year"></string>
<string name="cron_expression">Cron表达式</string>
<string name="cron_expression_tips">采用 Quartz Cron 表达式</string>
<string name="every_second">每秒钟</string>
<string name="every_minute">每分钟</string>
<string name="every_hour">每小时</string>
<string name="every_day">每日</string>
<string name="every_month">每月</string>
<string name="every_week">每周</string>
<string name="every_year">每年</string>
<string name="cyclic">周期</string>
<string name="cyclic_from"></string>
<string name="cyclic_from_week">从星期</string>
<string name="cyclic_to"></string>
<string name="start">起始</string>
<string name="end">结束</string>
<string name="interval_seconds_1"></string>
<string name="interval_seconds_2">秒开始,每</string>
<string name="interval_seconds_3">秒钟执行一次</string>
<string name="interval_minutes_1"></string>
<string name="interval_minutes_2">分开始,每隔</string>
<string name="interval_minutes_3">分钟执行一次</string>
<string name="interval_hours_1"></string>
<string name="interval_hours_2">时开始,每隔</string>
<string name="interval_hours_3">小时执行一次</string>
<string name="interval_days_1"></string>
<string name="interval_days_2">日开始,每隔</string>
<string name="interval_days_3">天执行一次</string>
<string name="interval_months_1"></string>
<string name="interval_months_2">月开始,每隔</string>
<string name="interval_months_3">月执行一次</string>
<string name="interval_years_1"></string>
<string name="interval_years_2">年开始,每隔</string>
<string name="interval_years_3">年执行一次</string>
<string name="assigned">指定</string>
<string name="not_assigned">不指定</string>
<string name="recent_day">最近工作日</string>
<string name="recent_day_1">每月</string>
<string name="recent_day_2">号最近的那个工作日</string>
<string name="last_day_of_month">本月最后一天</string>
<string name="last_day_of_month_recent_day">本月最后一个工作日</string>
<string name="weeks_of_week_1"></string>
<string name="weeks_of_week_2">周的星期</string>
<string name="last_week_of_month">本月最后</string>
<string name="last_week_of_month_1">本月最后一个星期</string>
<string name="last_week_of_month_2"></string>
<string name="cron_expression_check">Cron表达式测试结果</string>
<string name="invalid_cron_expression">Cron表达式无效\n%s</string>
<string name="next_execution_times">最近 %s 次运行时间:\n%s</string>
</resources>

@ -96,6 +96,29 @@
<!--<item name="android:orientation">horizontal</item>-->
</style>
<style name="taskBarStyle">
<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">10dp</item>
<item name="android:paddingTop">5dp</item>
<item name="android:paddingBottom">5dp</item>
<item name="android:background">@color/setting_bar_color</item>
<item name="android:gravity">center_vertical</item>
</style>
<style name="taskBarStyleWithSwitch">
<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:paddingTop">5dp</item>
<item name="android:paddingBottom">5dp</item>
<item name="android:background">@color/setting_bar_color</item>
<item name="android:gravity">center_vertical</item>
</style>
<style name="senderBarStyle">
<item name="android:layout_marginTop">5dp</item>
<item name="android:layout_marginStart">5dp</item>

Loading…
Cancel
Save