新增:自动任务·快捷指令 —— 到达地点&离开地点

This commit is contained in:
pppscn 2023-12-16 13:19:05 +08:00
parent d4ac2ed38e
commit f288f5a6dc
17 changed files with 215 additions and 116 deletions

View File

@ -88,7 +88,6 @@ class App : Application(), CactusCallback, Configuration.Provider by Core {
val LocationClient by lazy { LocationClient(context) } val LocationClient by lazy { LocationClient(context) }
val Geocoder by lazy { Geocoder(context) } val Geocoder by lazy { Geocoder(context) }
val DateFormat by lazy { SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault()) } val DateFormat by lazy { SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault()) }
val GeofenceStatusMap = mutableMapOf<Long, Boolean>() //是否已进入/离开围栏
} }
override fun attachBaseContext(base: Context) { override fun attachBaseContext(base: Context) {

View File

@ -73,11 +73,15 @@ class TaskPagingAdapter(private val itemClickListener: OnItemClickListener) : Pa
itemClickListener.onItemClicked(view, item) itemClickListener.onItemClicked(view, item)
} }
holder.binding.sbEnableTask.isChecked = item.status == 1 holder.binding.sbEnable.isChecked = item.status == 1
holder.binding.sbEnableTask.setOnCheckedChangeListener { view: View, isChecked -> holder.binding.sbEnable.setOnClickListener { view: View? ->
item.status = if (isChecked) 1 else 0
itemClickListener.onItemClicked(view, item) itemClickListener.onItemClicked(view, item)
} }
//不能用 setOnCheckedChangeListener否则会导致切换时状态错乱
/*holder.binding.sbEnable.setOnCheckedChangeListener { view: View, isChecked ->
item.status = if (isChecked) 1 else 0
itemClickListener.onItemClicked(view, item)
}*/
} else { } else {
holder.binding.layoutImage.visibility = View.VISIBLE holder.binding.layoutImage.visibility = View.VISIBLE
holder.binding.layoutIcons.visibility = View.GONE holder.binding.layoutIcons.visibility = View.GONE
@ -89,7 +93,7 @@ class TaskPagingAdapter(private val itemClickListener: OnItemClickListener) : Pa
holder.binding.ivStatus.setImageResource(item.statusImageId) holder.binding.ivStatus.setImageResource(item.statusImageId)
holder.binding.ivEdit.visibility = View.GONE holder.binding.ivEdit.visibility = View.GONE
holder.binding.ivDelete.visibility = View.GONE holder.binding.ivDelete.visibility = View.GONE
holder.binding.sbEnableTask.visibility = View.GONE holder.binding.sbEnable.visibility = View.GONE
} }
holder.binding.tvName.text = item.name holder.binding.tvName.text = item.name
holder.binding.tvDescription.text = item.description holder.binding.tvDescription.text = item.description

View File

@ -19,7 +19,7 @@ data class LocationInfo(
if (address != "") msg += "\n" + String.format(ResUtils.getString(R.string.location_address), address) if (address != "") msg += "\n" + String.format(ResUtils.getString(R.string.location_address), address)
if (time != "") msg += "\n" + String.format(ResUtils.getString(R.string.location_time), time) if (time != "") msg += "\n" + String.format(ResUtils.getString(R.string.location_time), time)
if (provider != "") msg += "\n" + String.format(ResUtils.getString(R.string.location_provider), provider) if (provider != "") msg += "\n" + String.format(ResUtils.getString(R.string.location_provider), provider)
return msg return msg + "\n"
} }
} }

View File

@ -1,11 +1,9 @@
package com.idormy.sms.forwarder.entity.task package com.idormy.sms.forwarder.entity.task
import com.idormy.sms.forwarder.R import com.idormy.sms.forwarder.R
import com.idormy.sms.forwarder.entity.LocationInfo
import com.idormy.sms.forwarder.utils.task.ConditionUtils.Companion.calculateDistance
import java.io.Serializable import java.io.Serializable
import kotlin.math.atan2
import kotlin.math.cos
import kotlin.math.sin
import kotlin.math.sqrt
data class LocationSetting( data class LocationSetting(
var description: String = "", //描述 var description: String = "", //描述
@ -25,12 +23,24 @@ data class LocationSetting(
} }
} }
fun calculateDistance(lat1: Double, lon1: Double, lat2: Double, lon2: Double): Double { //判断是否满足条件
val earthRadius = 6371000.0 // 地球平均半径,单位:米 fun isMatchCondition(locationOld: LocationInfo, locationNew: LocationInfo): Boolean {
val latDistance = Math.toRadians(lat2 - lat1) if (calcType == "distance") {
val lonDistance = Math.toRadians(lon2 - lon1) val distanceOld = calculateDistance(locationOld.latitude, locationOld.longitude, latitude, longitude)
val a = sin(latDistance / 2) * sin(latDistance / 2) + cos(Math.toRadians(lat1)) * cos(Math.toRadians(lat2)) * sin(lonDistance / 2) * sin(lonDistance / 2) val distanceNew = calculateDistance(locationNew.latitude, locationNew.longitude, latitude, longitude)
val c = 2 * atan2(sqrt(a), sqrt(1 - a)) if (type == "to" && distanceOld > distance && distanceNew <= distance) {
return earthRadius * c return true
} else if (type == "leave" && distanceOld <= distance && distanceNew > distance) {
return true
}
} else if (calcType == "address") {
if (type == "to" && !locationOld.address.contains(address) && locationNew.address.contains(address)) {
return true
} else if (type == "leave" && locationOld.address.contains(address) && !locationNew.address.contains(address)) {
return true
}
}
return false
} }
} }

View File

@ -1,5 +1,6 @@
package com.idormy.sms.forwarder.fragment package com.idormy.sms.forwarder.fragment
import android.util.Log
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
@ -104,7 +105,9 @@ class TasksFragment : BaseFragment<FragmentTasksBinding?>(), TaskPagingAdapter.O
override fun onItemClicked(view: View?, item: Task) { override fun onItemClicked(view: View?, item: Task) {
when (view?.id) { when (view?.id) {
R.id.sb_enable_task -> { R.id.sb_enable -> {
item.status = if (item.status == 0) 1 else 0
Log.d(TAG, "sb_enable: ${item.id}, ${item.status}")
viewModel.updateStatus(item.id, item.status) viewModel.updateStatus(item.id, item.status)
} }

View File

@ -45,7 +45,7 @@ class LocationService : Service(), Server.ServerListener {
super.onCreate() super.onCreate()
if (!SettingUtils.enableLocation) return if (!SettingUtils.enableLocation) return
startForegroundService() startService()
} }
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
@ -53,11 +53,11 @@ class LocationService : Service(), Server.ServerListener {
if (intent != null) { if (intent != null) {
when (intent.action) { when (intent.action) {
"START" -> { "START" -> {
startForegroundService() startService()
} }
"STOP" -> { "STOP" -> {
stopForegroundService() stopService()
} }
} }
} }
@ -70,7 +70,7 @@ class LocationService : Service(), Server.ServerListener {
super.onDestroy() super.onDestroy()
if (!SettingUtils.enableLocation) return if (!SettingUtils.enableLocation) return
stopForegroundService() stopService()
} }
override fun onException(e: Exception?) { override fun onException(e: Exception?) {
@ -85,9 +85,12 @@ class LocationService : Service(), Server.ServerListener {
Log.i(TAG, "onStopped: ") Log.i(TAG, "onStopped: ")
} }
private fun startForegroundService() { private fun startService() {
try { try {
//远程找手机 TODO: 判断权限 ACCESS_COARSE_LOCATION ACCESS_FINE_LOCATION //清空缓存
HttpServerUtils.apiLocationCache = LocationInfo()
TaskUtils.lastLocationInfo = LocationInfo()
if (SettingUtils.enableLocation && PermissionUtils.isGranted(android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION)) { if (SettingUtils.enableLocation && PermissionUtils.isGranted(android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION)) {
//可根据具体需求设置定位配置参数(这里只列出一些主要的参数) //可根据具体需求设置定位配置参数(这里只列出一些主要的参数)
val locationOption = App.LocationClient.getLocationOption().setAccuracy(SettingUtils.locationAccuracy)//设置位置精度:高精度 val locationOption = App.LocationClient.getLocationOption().setAccuracy(SettingUtils.locationAccuracy)//设置位置精度:高精度
@ -121,28 +124,15 @@ class LocationService : Service(), Server.ServerListener {
//TODO: 触发自动任务 //TODO: 触发自动任务
val locationInfoOld = TaskUtils.lastLocationInfo val locationInfoOld = TaskUtils.lastLocationInfo
TaskUtils.lastLocationInfo = locationInfoNew
if (locationInfoOld.longitude != locationInfoNew.longitude || locationInfoOld.latitude != locationInfoNew.latitude || locationInfoOld.address != locationInfoNew.address) { if (locationInfoOld.longitude != locationInfoNew.longitude || locationInfoOld.latitude != locationInfoNew.latitude || locationInfoOld.address != locationInfoNew.address) {
Log.d(TAG, "locationInfoOld = $locationInfoOld") Log.d(TAG, "locationInfoOld = $locationInfoOld")
TaskUtils.lastLocationInfo = locationInfoNew
val locationInfoJsonOld = Gson().toJson(locationInfoOld) val gson = Gson()
val locationInfoJsonNew = Gson().toJson(locationInfoNew) val locationJsonOld = gson.toJson(locationInfoOld)
val toAddressRequest = OneTimeWorkRequestBuilder<LocationWorker>().setInputData( val locationJsonNew = gson.toJson(locationInfoNew)
workDataOf( enqueueLocationWorkerRequest(TASK_CONDITION_TO_ADDRESS, locationJsonOld, locationJsonNew)
TaskWorker.conditionType to TASK_CONDITION_TO_ADDRESS, enqueueLocationWorkerRequest(TASK_CONDITION_LEAVE_ADDRESS, locationJsonOld, locationJsonNew)
"locationInfoJsonOld" to locationInfoJsonOld,
"locationInfoJsonNew" to locationInfoJsonNew,
)
).build()
WorkManager.getInstance(applicationContext).enqueue(toAddressRequest)
val leaveAddressRequest = OneTimeWorkRequestBuilder<LocationWorker>().setInputData(
workDataOf(
TaskWorker.conditionType to TASK_CONDITION_LEAVE_ADDRESS,
"locationInfoJsonOld" to locationInfoJsonNew,
"locationInfoJsonNew" to locationInfoJsonOld,
)
).build()
WorkManager.getInstance(applicationContext).enqueue(leaveAddressRequest)
} }
} }
@ -185,7 +175,11 @@ class LocationService : Service(), Server.ServerListener {
} }
} }
private fun stopForegroundService() { private fun stopService() {
//清空缓存
HttpServerUtils.apiLocationCache = LocationInfo()
TaskUtils.lastLocationInfo = LocationInfo()
isRunning = try { isRunning = try {
//如果已经开始定位,则先停止定位 //如果已经开始定位,则先停止定位
if (SettingUtils.enableLocation && App.LocationClient.isStarted()) { if (SettingUtils.enableLocation && App.LocationClient.isStarted()) {
@ -200,4 +194,20 @@ class LocationService : Service(), Server.ServerListener {
} }
} }
private fun enqueueLocationWorkerRequest(
conditionType: Int,
locationJsonOld: String,
locationJsonNew: String
) {
val locationWorkerRequest = OneTimeWorkRequestBuilder<LocationWorker>().setInputData(
workDataOf(
TaskWorker.conditionType to conditionType,
"locationJsonOld" to locationJsonOld,
"locationJsonNew" to locationJsonNew
)
).build()
WorkManager.getInstance(applicationContext).enqueue(locationWorkerRequest)
}
} }

View File

@ -0,0 +1,26 @@
package com.idormy.sms.forwarder.utils.task
import kotlin.math.atan2
import kotlin.math.cos
import kotlin.math.sin
import kotlin.math.sqrt
/**
* 自动任务条件工具类
*/
class ConditionUtils private constructor() {
companion object {
//计算两个经纬度之间的距离
fun calculateDistance(lat1: Double, lon1: Double, lat2: Double, lon2: Double): Double {
val earthRadius = 6371000.0 // 地球平均半径,单位:米
val latDistance = Math.toRadians(lat2 - lat1)
val lonDistance = Math.toRadians(lon2 - lon1)
val a = sin(latDistance / 2) * sin(latDistance / 2) + cos(Math.toRadians(lat1)) * cos(Math.toRadians(lat2)) * sin(lonDistance / 2) * sin(lonDistance / 2)
val c = 2 * atan2(sqrt(a), sqrt(1 - a))
return earthRadius * c
}
}
}

View File

@ -22,12 +22,12 @@ class CronJobScheduler {
val delayInMillis = task.nextExecTime.time / 1000 * 1000 - currentTimeMillis val delayInMillis = task.nextExecTime.time / 1000 * 1000 - currentTimeMillis
val inputData = Data.Builder().putLong(TaskWorker.taskId, task.id).build() val inputData = Data.Builder().putLong(TaskWorker.taskId, task.id).build()
val taskRequest = if (delayInMillis <= 0L) { val taskRequest = if (delayInMillis <= 0L) {
Log.d(TAG, "任务${task.id}立即执行delayInMillis = $delayInMillis") Log.d(TAG, "TASK-${task.id}立即执行delayInMillis = $delayInMillis")
OneTimeWorkRequestBuilder<CronWorker>() OneTimeWorkRequestBuilder<CronWorker>()
.setInputData(inputData) .setInputData(inputData)
.build() .build()
} else { } else {
Log.d(TAG, "任务${task.id}:延迟 $delayInMillis 毫秒执行") Log.d(TAG, "TASK-${task.id}:延迟 $delayInMillis 毫秒执行")
OneTimeWorkRequestBuilder<CronWorker>() OneTimeWorkRequestBuilder<CronWorker>()
.setInitialDelay(delayInMillis, TimeUnit.MILLISECONDS) .setInitialDelay(delayInMillis, TimeUnit.MILLISECONDS)
.setInputData(inputData) .setInputData(inputData)

View File

@ -46,24 +46,24 @@ class BatteryWorker(context: Context, params: WorkerParameters) : CoroutineWorke
// 根据任务信息执行相应操作 // 根据任务信息执行相应操作
val conditionList = Gson().fromJson(task.conditions, Array<TaskSetting>::class.java).toMutableList() val conditionList = Gson().fromJson(task.conditions, Array<TaskSetting>::class.java).toMutableList()
if (conditionList.isEmpty()) { if (conditionList.isEmpty()) {
Log.d(TAG, "任务${task.id}conditionList is empty") Log.d(TAG, "TASK-${task.id}conditionList is empty")
continue continue
} }
val firstCondition = conditionList.firstOrNull() val firstCondition = conditionList.firstOrNull()
if (firstCondition == null) { if (firstCondition == null) {
Log.d(TAG, "任务${task.id}firstCondition is null") Log.d(TAG, "TASK-${task.id}firstCondition is null")
continue continue
} }
val batterySetting = Gson().fromJson(firstCondition.setting, BatterySetting::class.java) val batterySetting = Gson().fromJson(firstCondition.setting, BatterySetting::class.java)
if (batterySetting == null) { if (batterySetting == null) {
Log.d(TAG, "任务${task.id}batterySetting is null") Log.d(TAG, "TASK-${task.id}batterySetting is null")
continue continue
} }
val msg = batterySetting.getMsg(status, levelNew, levelOld, TaskUtils.batteryInfo) val msg = batterySetting.getMsg(status, levelNew, levelOld, TaskUtils.batteryInfo)
if (msg.isEmpty()) { if (msg.isEmpty()) {
Log.d(TAG, "任务${task.id}msg is empty, batterySetting = $batterySetting, status = $status, levelNew = $levelNew, levelOld = $levelOld") Log.d(TAG, "TASK-${task.id}msg is empty, batterySetting = $batterySetting, status = $status, levelNew = $levelNew, levelOld = $levelOld")
continue continue
} }
@ -101,24 +101,24 @@ class BatteryWorker(context: Context, params: WorkerParameters) : CoroutineWorke
// 根据任务信息执行相应操作 // 根据任务信息执行相应操作
val conditionList = Gson().fromJson(task.conditions, Array<TaskSetting>::class.java).toMutableList() val conditionList = Gson().fromJson(task.conditions, Array<TaskSetting>::class.java).toMutableList()
if (conditionList.isEmpty()) { if (conditionList.isEmpty()) {
Log.d(TAG, "任务${task.id}conditionList is empty") Log.d(TAG, "TASK-${task.id}conditionList is empty")
continue continue
} }
val firstCondition = conditionList.firstOrNull() val firstCondition = conditionList.firstOrNull()
if (firstCondition == null) { if (firstCondition == null) {
Log.d(TAG, "任务${task.id}firstCondition is null") Log.d(TAG, "TASK-${task.id}firstCondition is null")
continue continue
} }
val chargeSetting = Gson().fromJson(firstCondition.setting, ChargeSetting::class.java) val chargeSetting = Gson().fromJson(firstCondition.setting, ChargeSetting::class.java)
if (chargeSetting == null) { if (chargeSetting == null) {
Log.d(TAG, "任务${task.id}chargeSetting is null") Log.d(TAG, "TASK-${task.id}chargeSetting is null")
continue continue
} }
val msg = chargeSetting.getMsg(statusNew, statusOld, pluggedNew, pluggedOld, TaskUtils.batteryInfo) val msg = chargeSetting.getMsg(statusNew, statusOld, pluggedNew, pluggedOld, TaskUtils.batteryInfo)
if (msg.isEmpty()) { if (msg.isEmpty()) {
Log.d(TAG, "任务${task.id}msg is empty, chargeSetting = $chargeSetting, statusNew = $statusNew, statusOld = $statusOld, pluggedNew = $pluggedNew, pluggedOld = $pluggedOld") Log.d(TAG, "TASK-${task.id}msg is empty, chargeSetting = $chargeSetting, statusNew = $statusNew, statusOld = $statusOld, pluggedNew = $pluggedNew, pluggedOld = $pluggedOld")
continue continue
} }

View File

@ -32,30 +32,30 @@ class CronWorker(context: Context, params: WorkerParameters) : CoroutineWorker(c
val task = AppDatabase.getInstance(App.context).taskDao().getOne(taskId) val task = AppDatabase.getInstance(App.context).taskDao().getOne(taskId)
if (task.status == 0) { if (task.status == 0) {
Log.d(TAG, "任务${task.id}task is disabled") Log.d(TAG, "TASK-${task.id}task is disabled")
return Result.success() return Result.success()
} }
// 根据任务信息执行相应操作 // 根据任务信息执行相应操作
val conditionList = Gson().fromJson(task.conditions, Array<TaskSetting>::class.java).toMutableList() val conditionList = Gson().fromJson(task.conditions, Array<TaskSetting>::class.java).toMutableList()
if (conditionList.isEmpty()) { if (conditionList.isEmpty()) {
Log.d(TAG, "任务${task.id}conditionList is empty") Log.d(TAG, "TASK-${task.id}conditionList is empty")
return Result.failure() return Result.failure()
} }
val firstCondition = conditionList.firstOrNull() val firstCondition = conditionList.firstOrNull()
if (firstCondition == null) { if (firstCondition == null) {
Log.d(TAG, "任务${task.id}firstCondition is null") Log.d(TAG, "TASK-${task.id}firstCondition is null")
return Result.failure() return Result.failure()
} }
val cronSetting = Gson().fromJson(firstCondition.setting, CronSetting::class.java) val cronSetting = Gson().fromJson(firstCondition.setting, CronSetting::class.java)
if (cronSetting == null) { if (cronSetting == null) {
Log.d(TAG, "任务${task.id}cronSetting is null") Log.d(TAG, "TASK-${task.id}cronSetting is null")
return Result.failure() return Result.failure()
} }
// TODO: 判断其他条件是否满足 // TODO: 判断其他条件是否满足
if (false) { if (false) {
Log.d(TAG, "任务${task.id}:其他条件不满足") Log.d(TAG, "TASK-${task.id}:其他条件不满足")
return Result.failure() return Result.failure()
} }
@ -67,7 +67,7 @@ class CronWorker(context: Context, params: WorkerParameters) : CoroutineWorker(c
// 将 nextExecTime 的毫秒部分设置为 0避免因为毫秒部分不同导致的任务重复执行 // 将 nextExecTime 的毫秒部分设置为 0避免因为毫秒部分不同导致的任务重复执行
nextExecTime.time = nextExecTime.time / 1000 * 1000 nextExecTime.time = nextExecTime.time / 1000 * 1000
task.nextExecTime = nextExecTime task.nextExecTime = nextExecTime
Log.d(TAG, "任务${task.id}lastExecTime = ${task.lastExecTime}, nextExecTime = ${task.nextExecTime}") Log.d(TAG, "TASK-${task.id}lastExecTime = ${task.lastExecTime}, nextExecTime = ${task.nextExecTime}")
// 自动禁用任务 // 自动禁用任务
if (task.nextExecTime.time / 1000 < now.time / 1000) { if (task.nextExecTime.time / 1000 < now.time / 1000) {
@ -78,7 +78,7 @@ class CronWorker(context: Context, params: WorkerParameters) : CoroutineWorker(c
AppDatabase.getInstance(App.context).taskDao().updateExecTime(task.id, task.lastExecTime, task.nextExecTime, task.status) AppDatabase.getInstance(App.context).taskDao().updateExecTime(task.id, task.lastExecTime, task.nextExecTime, task.status)
if (task.status == 0) { if (task.status == 0) {
Log.d(TAG, "任务${task.id}task is disabled") Log.d(TAG, "TASK-${task.id}task is disabled")
return Result.success() return Result.success()
} }

View File

@ -9,6 +9,7 @@ import androidx.work.WorkManager
import androidx.work.WorkerParameters import androidx.work.WorkerParameters
import com.google.gson.Gson import com.google.gson.Gson
import com.idormy.sms.forwarder.App import com.idormy.sms.forwarder.App
import com.idormy.sms.forwarder.R
import com.idormy.sms.forwarder.database.AppDatabase import com.idormy.sms.forwarder.database.AppDatabase
import com.idormy.sms.forwarder.entity.LocationInfo import com.idormy.sms.forwarder.entity.LocationInfo
import com.idormy.sms.forwarder.entity.MsgInfo import com.idormy.sms.forwarder.entity.MsgInfo
@ -17,6 +18,8 @@ import com.idormy.sms.forwarder.entity.task.TaskSetting
import com.idormy.sms.forwarder.utils.TASK_CONDITION_LEAVE_ADDRESS import com.idormy.sms.forwarder.utils.TASK_CONDITION_LEAVE_ADDRESS
import com.idormy.sms.forwarder.utils.TASK_CONDITION_TO_ADDRESS import com.idormy.sms.forwarder.utils.TASK_CONDITION_TO_ADDRESS
import com.idormy.sms.forwarder.utils.TaskWorker import com.idormy.sms.forwarder.utils.TaskWorker
import com.idormy.sms.forwarder.utils.task.ConditionUtils.Companion.calculateDistance
import com.xuexiang.xutil.resource.ResUtils.getString
import java.util.Date import java.util.Date
@Suppress("PrivatePropertyName", "DEPRECATION") @Suppress("PrivatePropertyName", "DEPRECATION")
@ -26,22 +29,24 @@ class LocationWorker(context: Context, params: WorkerParameters) : CoroutineWork
override suspend fun doWork(): Result { override suspend fun doWork(): Result {
Log.d(TAG, "doWork") val conditionType = inputData.getInt(TaskWorker.conditionType, -1)
val locationInfoJsonOld = inputData.getString("locationInfoJsonOld") val locationJsonOld = inputData.getString("locationJsonOld")
val locationInfoJsonNew = inputData.getString("locationInfoJsonNew") val locationJsonNew = inputData.getString("locationJsonNew")
if (locationInfoJsonOld == null || locationInfoJsonNew == null) { Log.d(TAG, "conditionType = $conditionType, locationJsonOld = $locationJsonOld, locationJsonNew = $locationJsonNew")
if (locationJsonOld == null || locationJsonNew == null) {
Log.d(TAG, "locationInfoOld or locationInfoNew is null") Log.d(TAG, "locationInfoOld or locationInfoNew is null")
return Result.failure() return Result.failure()
} }
val locationInfoOld = Gson().fromJson(locationInfoJsonOld, LocationInfo::class.java) val locationOld = Gson().fromJson(locationJsonOld, LocationInfo::class.java)
val locationInfoNew = Gson().fromJson(locationInfoJsonNew, LocationInfo::class.java) val locationNew = Gson().fromJson(locationJsonNew, LocationInfo::class.java)
if (locationInfoOld == null || locationInfoNew == null) { if (locationOld == null || locationNew == null) {
Log.d(TAG, "locationInfoOld or locationInfoNew is null") Log.d(TAG, "locationInfoOld or locationInfoNew is null")
return Result.failure() return Result.failure()
} }
when (val conditionType = inputData.getInt(TaskWorker.conditionType, -1)) { when (conditionType) {
//到达地点 //到达地点
TASK_CONDITION_TO_ADDRESS -> { TASK_CONDITION_TO_ADDRESS -> {
@ -52,26 +57,46 @@ class LocationWorker(context: Context, params: WorkerParameters) : CoroutineWork
// 根据任务信息执行相应操作 // 根据任务信息执行相应操作
val conditionList = Gson().fromJson(task.conditions, Array<TaskSetting>::class.java).toMutableList() val conditionList = Gson().fromJson(task.conditions, Array<TaskSetting>::class.java).toMutableList()
if (conditionList.isEmpty()) { if (conditionList.isEmpty()) {
Log.d(TAG, "任务${task.id}conditionList is empty") Log.d(TAG, "TASK-${task.id}conditionList is empty")
continue continue
} }
val firstCondition = conditionList.firstOrNull() val firstCondition = conditionList.firstOrNull()
if (firstCondition == null) { if (firstCondition == null) {
Log.d(TAG, "任务${task.id}firstCondition is null") Log.d(TAG, "TASK-${task.id}firstCondition is null")
continue continue
} }
val locationSetting = Gson().fromJson(firstCondition.setting, LocationSetting::class.java) val locationSetting = Gson().fromJson(firstCondition.setting, LocationSetting::class.java)
if (locationSetting == null) { if (locationSetting == null) {
Log.d(TAG, "任务${task.id}locationSetting is null") Log.d(TAG, "TASK-${task.id}locationSetting is null")
continue continue
} }
//TODO判断条件是否满足 //TODO判断条件是否满足
var description = locationSetting.description
val isMatchCondition = when (locationSetting.calcType) {
"distance" -> {
val distanceOld = calculateDistance(locationOld.latitude, locationOld.longitude, locationSetting.latitude, locationSetting.longitude)
val distanceNew = calculateDistance(locationNew.latitude, locationNew.longitude, locationSetting.latitude, locationSetting.longitude)
Log.d(TAG, "TASK-${task.id}distanceOld = $distanceOld, distanceNew = $distanceNew")
description += String.format(getString(R.string.current_distance_from_center), String.format("%.2f", distanceNew))
distanceOld > locationSetting.distance && distanceNew <= locationSetting.distance
}
"address" -> {
!locationOld.address.contains(locationSetting.address) && locationNew.address.contains(locationSetting.address)
}
else -> false
}
if (!isMatchCondition) {
Log.d(TAG, "TASK-${task.id}isMatchCondition = false")
continue
}
//TODO: 组装消息体 && 执行具体任务 //TODO: 组装消息体 && 执行具体任务
val msg = locationInfoNew.toString() val msgInfo = MsgInfo("task", task.name, locationNew.toString(), Date(), description)
val msgInfo = MsgInfo("task", task.name, msg, Date(), task.name)
val actionData = Data.Builder().putLong(TaskWorker.taskId, task.id).putString(TaskWorker.taskActions, task.actions).putString(TaskWorker.msgInfo, Gson().toJson(msgInfo)).build() val actionData = Data.Builder().putLong(TaskWorker.taskId, task.id).putString(TaskWorker.taskActions, task.actions).putString(TaskWorker.msgInfo, Gson().toJson(msgInfo)).build()
val actionRequest = OneTimeWorkRequestBuilder<ActionWorker>().setInputData(actionData).build() val actionRequest = OneTimeWorkRequestBuilder<ActionWorker>().setInputData(actionData).build()
WorkManager.getInstance().enqueue(actionRequest) WorkManager.getInstance().enqueue(actionRequest)
@ -89,26 +114,46 @@ class LocationWorker(context: Context, params: WorkerParameters) : CoroutineWork
// 根据任务信息执行相应操作 // 根据任务信息执行相应操作
val conditionList = Gson().fromJson(task.conditions, Array<TaskSetting>::class.java).toMutableList() val conditionList = Gson().fromJson(task.conditions, Array<TaskSetting>::class.java).toMutableList()
if (conditionList.isEmpty()) { if (conditionList.isEmpty()) {
Log.d(TAG, "任务${task.id}conditionList is empty") Log.d(TAG, "TASK-${task.id}conditionList is empty")
continue continue
} }
val firstCondition = conditionList.firstOrNull() val firstCondition = conditionList.firstOrNull()
if (firstCondition == null) { if (firstCondition == null) {
Log.d(TAG, "任务${task.id}firstCondition is null") Log.d(TAG, "TASK-${task.id}firstCondition is null")
continue continue
} }
val locationSetting = Gson().fromJson(firstCondition.setting, LocationSetting::class.java) val locationSetting = Gson().fromJson(firstCondition.setting, LocationSetting::class.java)
if (locationSetting == null) { if (locationSetting == null) {
Log.d(TAG, "任务${task.id}locationSetting is null") Log.d(TAG, "TASK-${task.id}locationSetting is null")
continue continue
} }
//TODO判断条件是否满足 //TODO判断条件是否满足
var description = locationSetting.description
val isMatchCondition = when (locationSetting.calcType) {
"distance" -> {
val distanceOld = calculateDistance(locationOld.latitude, locationOld.longitude, locationSetting.latitude, locationSetting.longitude)
val distanceNew = calculateDistance(locationNew.latitude, locationNew.longitude, locationSetting.latitude, locationSetting.longitude)
Log.d(TAG, "TASK-${task.id}distanceOld = $distanceOld, distanceNew = $distanceNew")
description += String.format(getString(R.string.current_distance_from_center), String.format("%.2f", distanceNew))
distanceOld <= locationSetting.distance && distanceNew > locationSetting.distance
}
"address" -> {
locationOld.address.contains(locationSetting.address) && !locationNew.address.contains(locationSetting.address)
}
else -> false
}
if (!isMatchCondition) {
Log.d(TAG, "TASK-${task.id}isMatchCondition = false")
continue
}
//TODO: 组装消息体 && 执行具体任务 //TODO: 组装消息体 && 执行具体任务
val msg = locationInfoNew.toString() val msgInfo = MsgInfo("task", task.name, locationNew.toString(), Date(), description)
val msgInfo = MsgInfo("task", task.name, msg, Date(), task.description)
val actionData = Data.Builder().putLong(TaskWorker.taskId, task.id).putString(TaskWorker.taskActions, task.actions).putString(TaskWorker.msgInfo, Gson().toJson(msgInfo)).build() val actionData = Data.Builder().putLong(TaskWorker.taskId, task.id).putString(TaskWorker.taskActions, task.actions).putString(TaskWorker.msgInfo, Gson().toJson(msgInfo)).build()
val actionRequest = OneTimeWorkRequestBuilder<ActionWorker>().setInputData(actionData).build() val actionRequest = OneTimeWorkRequestBuilder<ActionWorker>().setInputData(actionData).build()
WorkManager.getInstance().enqueue(actionRequest) WorkManager.getInstance().enqueue(actionRequest)

View File

@ -35,23 +35,23 @@ class LockScreenWorker(context: Context, params: WorkerParameters) : CoroutineWo
// 根据任务信息执行相应操作 // 根据任务信息执行相应操作
val conditionList = Gson().fromJson(task.conditions, Array<TaskSetting>::class.java).toMutableList() val conditionList = Gson().fromJson(task.conditions, Array<TaskSetting>::class.java).toMutableList()
if (conditionList.isEmpty()) { if (conditionList.isEmpty()) {
Log.d(TAG, "任务${task.id}conditionList is empty") Log.d(TAG, "TASK-${task.id}conditionList is empty")
continue continue
} }
val firstCondition = conditionList.firstOrNull() val firstCondition = conditionList.firstOrNull()
if (firstCondition == null) { if (firstCondition == null) {
Log.d(TAG, "任务${task.id}firstCondition is null") Log.d(TAG, "TASK-${task.id}firstCondition is null")
continue continue
} }
val lockScreenSetting = Gson().fromJson(firstCondition.setting, LockScreenSetting::class.java) val lockScreenSetting = Gson().fromJson(firstCondition.setting, LockScreenSetting::class.java)
if (lockScreenSetting == null) { if (lockScreenSetting == null) {
Log.d(TAG, "任务${task.id}lockScreenSetting is null") Log.d(TAG, "TASK-${task.id}lockScreenSetting is null")
continue continue
} }
if (action != lockScreenSetting.action) { if (action != lockScreenSetting.action) {
Log.d(TAG, "任务${task.id}action is not match, action = $action, lockScreenSetting = $lockScreenSetting") Log.d(TAG, "TASK-${task.id}action is not match, action = $action, lockScreenSetting = $lockScreenSetting")
continue continue
} }

View File

@ -39,23 +39,23 @@ class NetworkWorker(context: Context, params: WorkerParameters) : CoroutineWorke
// 根据任务信息执行相应操作 // 根据任务信息执行相应操作
val conditionList = Gson().fromJson(task.conditions, Array<TaskSetting>::class.java).toMutableList() val conditionList = Gson().fromJson(task.conditions, Array<TaskSetting>::class.java).toMutableList()
if (conditionList.isEmpty()) { if (conditionList.isEmpty()) {
Log.d(TAG, "任务${task.id}conditionList is empty") Log.d(TAG, "TASK-${task.id}conditionList is empty")
continue continue
} }
val firstCondition = conditionList.firstOrNull() val firstCondition = conditionList.firstOrNull()
if (firstCondition == null) { if (firstCondition == null) {
Log.d(TAG, "任务${task.id}firstCondition is null") Log.d(TAG, "TASK-${task.id}firstCondition is null")
continue continue
} }
val networkSetting = Gson().fromJson(firstCondition.setting, NetworkSetting::class.java) val networkSetting = Gson().fromJson(firstCondition.setting, NetworkSetting::class.java)
if (networkSetting == null) { if (networkSetting == null) {
Log.d(TAG, "任务${task.id}networkSetting is null") Log.d(TAG, "TASK-${task.id}networkSetting is null")
continue continue
} }
if (TaskUtils.networkState != networkSetting.networkState) { if (TaskUtils.networkState != networkSetting.networkState) {
Log.d(TAG, "任务${task.id}networkState is not match, networkSetting = $networkSetting") Log.d(TAG, "TASK-${task.id}networkState is not match, networkSetting = $networkSetting")
continue continue
} }
@ -70,7 +70,7 @@ class NetworkWorker(context: Context, params: WorkerParameters) : CoroutineWorke
1 -> { 1 -> {
val dataSimSlot = TaskUtils.dataSimSlot val dataSimSlot = TaskUtils.dataSimSlot
if (networkSetting.dataSimSlot != 0 && dataSimSlot != networkSetting.dataSimSlot) { if (networkSetting.dataSimSlot != 0 && dataSimSlot != networkSetting.dataSimSlot) {
Log.d(TAG, "任务${task.id}dataSimSlot is not match, networkSetting = $networkSetting") Log.d(TAG, "TASK-${task.id}dataSimSlot is not match, networkSetting = $networkSetting")
continue continue
} }
msg.append(getString(R.string.net_mobile)).append("\n") msg.append(getString(R.string.net_mobile)).append("\n")
@ -93,7 +93,7 @@ class NetworkWorker(context: Context, params: WorkerParameters) : CoroutineWorke
//WiFi //WiFi
2 -> { 2 -> {
if (networkSetting.wifiSsid.isNotEmpty() && TaskUtils.wifiSsid != networkSetting.wifiSsid) { if (networkSetting.wifiSsid.isNotEmpty() && TaskUtils.wifiSsid != networkSetting.wifiSsid) {
Log.d(TAG, "任务${task.id}wifiSsid is not match, networkSetting = $networkSetting") Log.d(TAG, "TASK-${task.id}wifiSsid is not match, networkSetting = $networkSetting")
continue continue
} }
msg.append(getString(R.string.net_wifi)).append("\n") msg.append(getString(R.string.net_wifi)).append("\n")

View File

@ -36,29 +36,29 @@ class SimWorker(context: Context, params: WorkerParameters) : CoroutineWorker(co
// 根据任务信息执行相应操作 // 根据任务信息执行相应操作
val conditionList = Gson().fromJson(task.conditions, Array<TaskSetting>::class.java).toMutableList() val conditionList = Gson().fromJson(task.conditions, Array<TaskSetting>::class.java).toMutableList()
if (conditionList.isEmpty()) { if (conditionList.isEmpty()) {
Log.d(TAG, "任务${task.id}conditionList is empty") Log.d(TAG, "TASK-${task.id}conditionList is empty")
continue continue
} }
val firstCondition = conditionList.firstOrNull() val firstCondition = conditionList.firstOrNull()
if (firstCondition == null) { if (firstCondition == null) {
Log.d(TAG, "任务${task.id}firstCondition is null") Log.d(TAG, "TASK-${task.id}firstCondition is null")
continue continue
} }
val simSetting = Gson().fromJson(firstCondition.setting, SimSetting::class.java) val simSetting = Gson().fromJson(firstCondition.setting, SimSetting::class.java)
if (simSetting == null) { if (simSetting == null) {
Log.d(TAG, "任务${task.id}simSetting is null") Log.d(TAG, "TASK-${task.id}simSetting is null")
continue continue
} }
if (TaskUtils.simState != simSetting.simState) { if (TaskUtils.simState != simSetting.simState) {
Log.d(TAG, "任务${task.id}networkState is not match, simSetting = $simSetting") Log.d(TAG, "TASK-${task.id}networkState is not match, simSetting = $simSetting")
continue continue
} }
//TODO判断其他条件是否满足注意SIM卡已准备就绪延迟5秒才进入这里给够搜索信号时间 //TODO判断其他条件是否满足注意SIM卡已准备就绪延迟5秒才进入这里给够搜索信号时间
/*if (!TaskUtils.isConditionMatch(task, msg)) { /*if (!TaskUtils.isConditionMatch(task, msg)) {
Log.d(TAG, "任务${task.id}condition is not match") Log.d(TAG, "TASK-${task.id}condition is not match")
continue continue
}*/ }*/

View File

@ -45,7 +45,7 @@
android:orientation="horizontal" /> android:orientation="horizontal" />
<com.xuexiang.xui.widget.button.switchbutton.SwitchButton <com.xuexiang.xui.widget.button.switchbutton.SwitchButton
android:id="@+id/sb_enable_task" android:id="@+id/sb_enable"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:paddingTop="@dimen/config_padding_5dp" android:paddingTop="@dimen/config_padding_5dp"
@ -119,9 +119,15 @@
<LinearLayout <LinearLayout
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:gravity="center_horizontal" android:gravity="bottom|end"
android:orientation="vertical"> android:orientation="vertical">
<TextView
android:id="@+id/tv_time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end" />
<LinearLayout <LinearLayout
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@ -129,8 +135,8 @@
<ImageView <ImageView
android:id="@+id/iv_copy" android:id="@+id/iv_copy"
android:layout_width="28dp" android:layout_width="@dimen/card_view_image_size"
android:layout_height="28dp" android:layout_height="@dimen/card_view_image_size"
android:padding="@dimen/card_view_image_padding" android:padding="@dimen/card_view_image_padding"
android:scaleType="fitCenter" android:scaleType="fitCenter"
android:src="@drawable/ic_copy" android:src="@drawable/ic_copy"
@ -139,8 +145,8 @@
<ImageView <ImageView
android:id="@+id/iv_edit" android:id="@+id/iv_edit"
android:layout_width="28dp" android:layout_width="@dimen/card_view_image_size"
android:layout_height="28dp" android:layout_height="@dimen/card_view_image_size"
android:padding="@dimen/card_view_image_padding" android:padding="@dimen/card_view_image_padding"
android:scaleType="fitCenter" android:scaleType="fitCenter"
android:src="@drawable/ic_edit" android:src="@drawable/ic_edit"
@ -149,8 +155,8 @@
<ImageView <ImageView
android:id="@+id/iv_delete" android:id="@+id/iv_delete"
android:layout_width="28dp" android:layout_width="@dimen/card_view_image_size"
android:layout_height="28dp" android:layout_height="@dimen/card_view_image_size"
android:padding="@dimen/card_view_image_padding" android:padding="@dimen/card_view_image_padding"
android:scaleType="fitCenter" android:scaleType="fitCenter"
android:src="@drawable/ic_delete" android:src="@drawable/ic_delete"
@ -159,12 +165,6 @@
</LinearLayout> </LinearLayout>
<TextView
android:id="@+id/tv_time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="5dp" />
</LinearLayout> </LinearLayout>
</LinearLayout> </LinearLayout>

View File

@ -1235,13 +1235,14 @@
<string name="keyword_leave_address_2"> = leaved</string> <string name="keyword_leave_address_2"> = leaved</string>
<string name="calc_type_distance_error">Latitude and longitude or distance cannot be empty.</string> <string name="calc_type_distance_error">Latitude and longitude or distance cannot be empty.</string>
<string name="calc_type_address_error">Address keyword cannot be empty.</string> <string name="calc_type_address_error">Address keyword cannot be empty.</string>
<string name="to_address_distance_description">Around longitude %s, latitude %s, within a %s-meter radius.</string> <string name="to_address_distance_description">Entering area centered at (%s, %s) with a radius of %s-meter.</string>
<string name="to_address_keyword_description">GPS address contains %s means arrival.</string> <string name="to_address_keyword_description">GPS address contains %s means arrival.</string>
<string name="leave_address_distance_description">Leave around longitude %s, latitude %s, within a %s-meter radius.</string> <string name="leave_address_distance_description">Leave area centered at (%s, %s) with a radius of %s-meter.</string>
<string name="leave_address_keyword_description">GPS address NOT contains %s means leaved.</string> <string name="leave_address_keyword_description">GPS address NOT contains %s means leaved.</string>
<string name="condition_already_exists">This type of condition already exists.</string> <string name="condition_already_exists">This type of condition already exists.</string>
<string name="action_already_exists">This type of action already exists.</string> <string name="action_already_exists">This type of action already exists.</string>
<string name="only_one_location_condition">Only one condition, either "To Address" or "Leave Address" can be added.</string> <string name="only_one_location_condition">"To Address" vs "Leave Address": mutually exclusive.</string>
<string name="current_address">Current Address: %s</string> <string name="current_address">Current Address: %s</string>
<string name="location_failed">Location failed. Please try again later.</string> <string name="location_failed">Location failed. Please try again later.</string>
<string name="current_distance_from_center">, %s meters from the center.</string>
</resources> </resources>

View File

@ -1236,13 +1236,14 @@
<string name="keyword_leave_address_2">则表示离开</string> <string name="keyword_leave_address_2">则表示离开</string>
<string name="calc_type_distance_error">经纬度或距离都不能为空</string> <string name="calc_type_distance_error">经纬度或距离都不能为空</string>
<string name="calc_type_address_error">地址关键字不能为空</string> <string name="calc_type_address_error">地址关键字不能为空</string>
<string name="to_address_distance_description">进入以经度:%s,维度:%s为中心,%s米半径的区域</string> <string name="to_address_distance_description">进入以经纬度(%s,%s)为中心, %s米为半径的区域</string>
<string name="to_address_keyword_description">进入GPS地址包含[%s]关键字区域</string> <string name="to_address_keyword_description">进入GPS地址包含[%s]关键字区域</string>
<string name="leave_address_distance_description">离开以经度:%s,维度:%s为中心,%s米半径的区域</string> <string name="leave_address_distance_description">离开以经纬度(%s,%s)为中心, %s米为半径的区域</string>
<string name="leave_address_keyword_description">离开GPS地址包含[%s]关键字区域</string> <string name="leave_address_keyword_description">离开GPS地址包含[%s]关键字区域</string>
<string name="condition_already_exists">添加过该类型条件</string> <string name="condition_already_exists">已添加过该类型条件</string>
<string name="action_already_exists">添加过该类型动作</string> <string name="action_already_exists">已添加过该类型动作</string>
<string name="only_one_location_condition">只能添加一个 进入地点 或 离开地址 类型条件</string> <string name="only_one_location_condition">进入地点 与 离开地址 类型条件互斥</string>
<string name="current_address">当前地址:%s</string> <string name="current_address">当前地址:%s</string>
<string name="location_failed">定位失败,请稍后重试</string> <string name="location_failed">定位失败,请稍后重试</string>
<string name="current_distance_from_center">, 当前距离中心%s米</string>
</resources> </resources>