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

pull/408/head
pppscn 10 months ago
parent 0d77eac6ce
commit d4ac2ed38e

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

@ -72,6 +72,12 @@ class TaskPagingAdapter(private val itemClickListener: OnItemClickListener) : Pa
holder.binding.ivDelete.setOnClickListener { view: View? ->
itemClickListener.onItemClicked(view, item)
}
holder.binding.sbEnableTask.isChecked = item.status == 1
holder.binding.sbEnableTask.setOnCheckedChangeListener { view: View, isChecked ->
item.status = if (isChecked) 1 else 0
itemClickListener.onItemClicked(view, item)
}
} else {
holder.binding.layoutImage.visibility = View.VISIBLE
holder.binding.layoutIcons.visibility = View.GONE
@ -83,6 +89,7 @@ class TaskPagingAdapter(private val itemClickListener: OnItemClickListener) : Pa
holder.binding.ivStatus.setImageResource(item.statusImageId)
holder.binding.ivEdit.visibility = View.GONE
holder.binding.ivDelete.visibility = View.GONE
holder.binding.sbEnableTask.visibility = View.GONE
}
holder.binding.tvName.text = item.name
holder.binding.tvDescription.text = item.description

@ -59,4 +59,7 @@ interface TaskDao {
@Query("DELETE FROM Task")
fun deleteAll()
@Query("UPDATE Task SET status = :status WHERE id = :id")
fun updateStatus(id: Long, status: Int)
}

@ -38,4 +38,8 @@ class TaskViewModel(private val dao: TaskDao) : ViewModel() {
fun delete(id: Long) = ioThread {
dao.delete(id)
}
fun updateStatus(id: Long, status: Int) = ioThread {
dao.updateStatus(id, status)
}
}

@ -25,9 +25,7 @@ data class LocationSetting(
}
}
fun calculateDistance(
lat1: Double, lon1: Double, lat2: Double, lon2: Double
): Double {
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)

@ -13,6 +13,7 @@ import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.bottomsheet.BottomSheetDialog
import com.google.gson.Gson
import com.idormy.sms.forwarder.App
import com.idormy.sms.forwarder.R
import com.idormy.sms.forwarder.adapter.ItemMoveCallback
import com.idormy.sms.forwarder.adapter.TaskSettingAdapter
@ -25,6 +26,7 @@ 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.service.LocationService
import com.idormy.sms.forwarder.utils.*
import com.idormy.sms.forwarder.utils.task.CronJobScheduler
import com.xuexiang.xaop.annotation.SingleClick
@ -39,6 +41,8 @@ import com.xuexiang.xui.utils.DensityUtils
import com.xuexiang.xui.utils.WidgetUtils
import com.xuexiang.xui.widget.actionbar.TitleBar
import com.xuexiang.xui.widget.alpha.XUIAlphaTextView
import com.xuexiang.xui.widget.dialog.materialdialog.DialogAction
import com.xuexiang.xui.widget.dialog.materialdialog.MaterialDialog
import gatewayapps.crondroid.CronExpression
import io.reactivex.SingleObserver
import io.reactivex.android.schedulers.AndroidSchedulers
@ -363,6 +367,25 @@ class TasksEditFragment : BaseFragment<FragmentTasksEditBinding?>(), View.OnClic
XToastUtils.error(getString(R.string.condition_already_exists))
return
}
//必须开启定位服务,才能使用进入地点 或 离开地址 类型条件
if ((typeCondition == TASK_CONDITION_TO_ADDRESS || typeCondition == TASK_CONDITION_LEAVE_ADDRESS) && !App.LocationClient.isStarted()) {
MaterialDialog.Builder(requireContext())
.iconRes(R.drawable.auto_task_icon_location)
.title(R.string.enable_location)
.content(R.string.enable_location_dialog)
.cancelable(false)
.positiveText(R.string.lab_yes)
.negativeText(R.string.lab_no).onPositive { _: MaterialDialog?, _: DialogAction? ->
SettingUtils.enableLocation = true
val serviceIntent = Intent(requireContext(), LocationService::class.java)
serviceIntent.action = "START"
requireContext().startService(serviceIntent)
}.show()
return
}
//进入地点 或 离开地址 类型条件互斥
if ((typeCondition == TASK_CONDITION_TO_ADDRESS || typeCondition == TASK_CONDITION_LEAVE_ADDRESS) && (item.type == TASK_CONDITION_TO_ADDRESS || item.type == TASK_CONDITION_LEAVE_ADDRESS)) {
XToastUtils.error(getString(R.string.only_one_location_condition))
return

@ -104,6 +104,10 @@ class TasksFragment : BaseFragment<FragmentTasksBinding?>(), TaskPagingAdapter.O
override fun onItemClicked(view: View?, item: Task) {
when (view?.id) {
R.id.sb_enable_task -> {
viewModel.updateStatus(item.id, item.status)
}
R.id.iv_copy -> {
PageOption.to(TasksEditFragment::class.java)
.setNewActivity(true).putLong(KEY_TASK_ID, item.id)

@ -7,14 +7,18 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import com.google.gson.Gson
import com.idormy.sms.forwarder.App
import com.idormy.sms.forwarder.R
import com.idormy.sms.forwarder.core.BaseFragment
import com.idormy.sms.forwarder.databinding.FragmentTasksConditionLeaveAddressBinding
import com.idormy.sms.forwarder.entity.task.LocationSetting
import com.idormy.sms.forwarder.service.LocationService
import com.idormy.sms.forwarder.utils.HttpServerUtils
import com.idormy.sms.forwarder.utils.KEY_BACK_DATA_CONDITION
import com.idormy.sms.forwarder.utils.KEY_BACK_DESCRIPTION_CONDITION
import com.idormy.sms.forwarder.utils.KEY_EVENT_DATA_CONDITION
import com.idormy.sms.forwarder.utils.KEY_TEST_CONDITION
import com.idormy.sms.forwarder.utils.SettingUtils
import com.idormy.sms.forwarder.utils.TASK_CONDITION_LEAVE_ADDRESS
import com.idormy.sms.forwarder.utils.XToastUtils
import com.jeremyliao.liveeventbus.LiveEventBus
@ -24,6 +28,8 @@ import com.xuexiang.xrouter.annotation.AutoWired
import com.xuexiang.xrouter.launcher.XRouter
import com.xuexiang.xui.utils.CountDownButtonHelper
import com.xuexiang.xui.widget.actionbar.TitleBar
import com.xuexiang.xui.widget.dialog.materialdialog.DialogAction
import com.xuexiang.xui.widget.dialog.materialdialog.MaterialDialog
@Page(name = "LeaveAddress")
@Suppress("PrivatePropertyName")
@ -98,6 +104,7 @@ class LeaveAddressFragment : BaseFragment<FragmentTasksConditionLeaveAddressBind
binding!!.btnTest.setOnClickListener(this)
binding!!.btnDel.setOnClickListener(this)
binding!!.btnSave.setOnClickListener(this)
binding!!.btnCurrentCoordinates.setOnClickListener(this)
LiveEventBus.get(KEY_TEST_CONDITION, String::class.java).observe(this) {
mCountDownHelper?.finish()
@ -113,6 +120,34 @@ class LeaveAddressFragment : BaseFragment<FragmentTasksConditionLeaveAddressBind
override fun onClick(v: View) {
try {
when (v.id) {
R.id.btn_current_coordinates -> {
if (!App.LocationClient.isStarted()) {
MaterialDialog.Builder(requireContext())
.iconRes(R.drawable.auto_task_icon_location)
.title(R.string.enable_location)
.content(R.string.enable_location_dialog)
.cancelable(false)
.positiveText(R.string.lab_yes)
.negativeText(R.string.lab_no).onPositive { _: MaterialDialog?, _: DialogAction? ->
SettingUtils.enableLocation = true
val serviceIntent = Intent(requireContext(), LocationService::class.java)
serviceIntent.action = "START"
requireContext().startService(serviceIntent)
}.show()
return
}
val location = HttpServerUtils.apiLocationCache
if (location.latitude == 0.0 || location.longitude == 0.0) {
XToastUtils.error(getString(R.string.location_failed), 30000)
return
}
binding!!.etLatitude.setText(location.latitude.toString())
binding!!.etLongitude.setText(location.longitude.toString())
XToastUtils.success(String.format(getString(R.string.current_address), location.address), 30000)
}
R.id.btn_test -> {
mCountDownHelper?.start()
Thread {

@ -7,14 +7,18 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import com.google.gson.Gson
import com.idormy.sms.forwarder.App
import com.idormy.sms.forwarder.R
import com.idormy.sms.forwarder.core.BaseFragment
import com.idormy.sms.forwarder.databinding.FragmentTasksConditionToAddressBinding
import com.idormy.sms.forwarder.entity.task.LocationSetting
import com.idormy.sms.forwarder.service.LocationService
import com.idormy.sms.forwarder.utils.HttpServerUtils
import com.idormy.sms.forwarder.utils.KEY_BACK_DATA_CONDITION
import com.idormy.sms.forwarder.utils.KEY_BACK_DESCRIPTION_CONDITION
import com.idormy.sms.forwarder.utils.KEY_EVENT_DATA_CONDITION
import com.idormy.sms.forwarder.utils.KEY_TEST_CONDITION
import com.idormy.sms.forwarder.utils.SettingUtils
import com.idormy.sms.forwarder.utils.TASK_CONDITION_TO_ADDRESS
import com.idormy.sms.forwarder.utils.XToastUtils
import com.jeremyliao.liveeventbus.LiveEventBus
@ -24,6 +28,8 @@ import com.xuexiang.xrouter.annotation.AutoWired
import com.xuexiang.xrouter.launcher.XRouter
import com.xuexiang.xui.utils.CountDownButtonHelper
import com.xuexiang.xui.widget.actionbar.TitleBar
import com.xuexiang.xui.widget.dialog.materialdialog.DialogAction
import com.xuexiang.xui.widget.dialog.materialdialog.MaterialDialog
@Page(name = "ToAddress")
@Suppress("PrivatePropertyName")
@ -98,6 +104,7 @@ class ToAddressFragment : BaseFragment<FragmentTasksConditionToAddressBinding?>(
binding!!.btnTest.setOnClickListener(this)
binding!!.btnDel.setOnClickListener(this)
binding!!.btnSave.setOnClickListener(this)
binding!!.btnCurrentCoordinates.setOnClickListener(this)
LiveEventBus.get(KEY_TEST_CONDITION, String::class.java).observe(this) {
mCountDownHelper?.finish()
@ -113,6 +120,34 @@ class ToAddressFragment : BaseFragment<FragmentTasksConditionToAddressBinding?>(
override fun onClick(v: View) {
try {
when (v.id) {
R.id.btn_current_coordinates -> {
if (!App.LocationClient.isStarted()) {
MaterialDialog.Builder(requireContext())
.iconRes(R.drawable.auto_task_icon_location)
.title(R.string.enable_location)
.content(R.string.enable_location_dialog)
.cancelable(false)
.positiveText(R.string.lab_yes)
.negativeText(R.string.lab_no).onPositive { _: MaterialDialog?, _: DialogAction? ->
SettingUtils.enableLocation = true
val serviceIntent = Intent(requireContext(), LocationService::class.java)
serviceIntent.action = "START"
requireContext().startService(serviceIntent)
}.show()
return
}
val location = HttpServerUtils.apiLocationCache
if (location.latitude == 0.0 || location.longitude == 0.0) {
XToastUtils.error(getString(R.string.location_failed), 30000)
return
}
binding!!.etLatitude.setText(location.latitude.toString())
binding!!.etLongitude.setText(location.longitude.toString())
XToastUtils.success(String.format(getString(R.string.current_address), location.address), 30000)
}
R.id.btn_test -> {
mCountDownHelper?.start()
Thread {

@ -6,10 +6,19 @@ import android.content.Intent
import android.location.Location
import android.os.IBinder
import android.util.Log
import androidx.work.OneTimeWorkRequestBuilder
import androidx.work.WorkManager
import androidx.work.workDataOf
import com.google.gson.Gson
import com.idormy.sms.forwarder.App
import com.idormy.sms.forwarder.entity.LocationInfo
import com.idormy.sms.forwarder.utils.HttpServerUtils
import com.idormy.sms.forwarder.utils.SettingUtils
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.TaskWorker
import com.idormy.sms.forwarder.utils.task.TaskUtils
import com.idormy.sms.forwarder.workers.LocationWorker
import com.king.location.LocationErrorCode
import com.king.location.OnExceptionListener
import com.king.location.OnLocationListener
@ -97,20 +106,44 @@ class LocationService : Service(), Server.ServerListener {
//位置信息
Log.d(TAG, "onLocationChanged(location = ${location})")
val locationInfo = LocationInfo(
val locationInfoNew = LocationInfo(
location.longitude, location.latitude, "", App.DateFormat.format(Date(location.time)), location.provider.toString()
)
//根据坐标经纬度获取位置地址信息WGS-84坐标系
val list = App.Geocoder.getFromLocation(location.latitude, location.longitude, 1)
if (list?.isNotEmpty() == true) {
locationInfo.address = list[0].getAddressLine(0)
locationInfoNew.address = list[0].getAddressLine(0)
}
Log.d(TAG, "locationInfo = $locationInfo")
HttpServerUtils.apiLocationCache = locationInfo
Log.d(TAG, "locationInfoNew = $locationInfoNew")
HttpServerUtils.apiLocationCache = locationInfoNew
//TODO: 触发自动任务
val locationInfoOld = TaskUtils.lastLocationInfo
TaskUtils.lastLocationInfo = locationInfoNew
if (locationInfoOld.longitude != locationInfoNew.longitude || locationInfoOld.latitude != locationInfoNew.latitude || locationInfoOld.address != locationInfoNew.address) {
Log.d(TAG, "locationInfoOld = $locationInfoOld")
val locationInfoJsonOld = Gson().toJson(locationInfoOld)
val locationInfoJsonNew = Gson().toJson(locationInfoNew)
val toAddressRequest = OneTimeWorkRequestBuilder<LocationWorker>().setInputData(
workDataOf(
TaskWorker.conditionType to TASK_CONDITION_TO_ADDRESS,
"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)
}
}
override fun onProviderEnabled(provider: String) {

@ -2,6 +2,7 @@ package com.idormy.sms.forwarder.utils.task
import android.os.BatteryManager
import com.idormy.sms.forwarder.R
import com.idormy.sms.forwarder.entity.LocationInfo
import com.idormy.sms.forwarder.utils.SP_BATTERY_INFO
import com.idormy.sms.forwarder.utils.SP_BATTERY_LEVEL
import com.idormy.sms.forwarder.utils.SP_BATTERY_PCT
@ -104,5 +105,9 @@ class TaskUtils private constructor() {
//SIM卡状态0-未知状态1-卡被移除5-卡已准备就绪
var simState: Int by SharedPreference(SP_SIM_STATE, 0)
//上次定位信息
var lastLocationInfo: LocationInfo by SharedPreference("lastLocationInfo", LocationInfo())
}
}

@ -10,14 +10,13 @@ import androidx.work.WorkerParameters
import com.google.gson.Gson
import com.idormy.sms.forwarder.App
import com.idormy.sms.forwarder.database.AppDatabase
import com.idormy.sms.forwarder.entity.LocationInfo
import com.idormy.sms.forwarder.entity.MsgInfo
import com.idormy.sms.forwarder.entity.task.BatterySetting
import com.idormy.sms.forwarder.entity.task.ChargeSetting
import com.idormy.sms.forwarder.entity.task.LocationSetting
import com.idormy.sms.forwarder.entity.task.TaskSetting
import com.idormy.sms.forwarder.utils.TASK_CONDITION_BATTERY
import com.idormy.sms.forwarder.utils.TASK_CONDITION_CHARGE
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.TaskWorker
import com.idormy.sms.forwarder.utils.task.TaskUtils
import java.util.Date
@Suppress("PrivatePropertyName", "DEPRECATION")
@ -27,18 +26,25 @@ class LocationWorker(context: Context, params: WorkerParameters) : CoroutineWork
override suspend fun doWork(): Result {
when (val conditionType = inputData.getInt(TaskWorker.conditionType, -1)) {
Log.d(TAG, "doWork")
val locationInfoJsonOld = inputData.getString("locationInfoJsonOld")
val locationInfoJsonNew = inputData.getString("locationInfoJsonNew")
if (locationInfoJsonOld == null || locationInfoJsonNew == null) {
Log.d(TAG, "locationInfoOld or locationInfoNew is null")
return Result.failure()
}
TASK_CONDITION_BATTERY -> {
val status = inputData.getInt("status", -1)
val levelNew = inputData.getInt("level_new", -1)
val levelOld = inputData.getInt("level_old", -1)
Log.d(TAG, "levelNew: $levelNew, levelOld: $levelOld")
if (levelNew == -1 || levelOld == -1) {
Log.d(TAG, "levelNew or levelOld is -1")
return Result.failure()
}
val locationInfoOld = Gson().fromJson(locationInfoJsonOld, LocationInfo::class.java)
val locationInfoNew = Gson().fromJson(locationInfoJsonNew, LocationInfo::class.java)
if (locationInfoOld == null || locationInfoNew == null) {
Log.d(TAG, "locationInfoOld or locationInfoNew is null")
return Result.failure()
}
when (val conditionType = inputData.getInt(TaskWorker.conditionType, -1)) {
//到达地点
TASK_CONDITION_TO_ADDRESS -> {
val taskList = AppDatabase.getInstance(App.context).taskDao().getByType(conditionType)
for (task in taskList) {
Log.d(TAG, "task = $task")
@ -55,27 +61,18 @@ class LocationWorker(context: Context, params: WorkerParameters) : CoroutineWork
continue
}
val batterySetting = Gson().fromJson(firstCondition.setting, BatterySetting::class.java)
if (batterySetting == null) {
Log.d(TAG, "任务${task.id}batterySetting is null")
continue
}
val msg = batterySetting.getMsg(status, levelNew, levelOld, TaskUtils.batteryInfo)
if (msg.isEmpty()) {
Log.d(TAG, "任务${task.id}msg is empty, batterySetting = $batterySetting, status = $status, levelNew = $levelNew, levelOld = $levelOld")
val locationSetting = Gson().fromJson(firstCondition.setting, LocationSetting::class.java)
if (locationSetting == null) {
Log.d(TAG, "任务${task.id}locationSetting is null")
continue
}
//TODO判断其他条件是否满足
//TODO判断条件是否满足
//TODO: 组装消息体 && 执行具体任务
val msg = locationInfoNew.toString()
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()
WorkManager.getInstance().enqueue(actionRequest)
}
@ -83,17 +80,8 @@ class LocationWorker(context: Context, params: WorkerParameters) : CoroutineWork
return Result.success()
}
TASK_CONDITION_CHARGE -> {
val statusNew = inputData.getInt("status_new", -1)
val statusOld = inputData.getInt("status_old", -1)
val pluggedNew = inputData.getInt("plugged_new", -1)
val pluggedOld = inputData.getInt("plugged_old", -1)
Log.d(TAG, "statusNew: $statusNew, statusOld: $statusOld, pluggedNew: $pluggedNew, pluggedOld: $pluggedOld")
if (statusNew == -1 || statusOld == -1 || pluggedNew == -1 || pluggedOld == -1) {
Log.d(TAG, "statusNew or statusOld or pluggedNew or pluggedOld is -1")
return Result.failure()
}
//离开地点
TASK_CONDITION_LEAVE_ADDRESS -> {
val taskList = AppDatabase.getInstance(App.context).taskDao().getByType(conditionType)
for (task in taskList) {
Log.d(TAG, "task = $task")
@ -110,27 +98,18 @@ class LocationWorker(context: Context, params: WorkerParameters) : CoroutineWork
continue
}
val chargeSetting = Gson().fromJson(firstCondition.setting, ChargeSetting::class.java)
if (chargeSetting == null) {
Log.d(TAG, "任务${task.id}chargeSetting is null")
continue
}
val msg = chargeSetting.getMsg(statusNew, statusOld, pluggedNew, pluggedOld, TaskUtils.batteryInfo)
if (msg.isEmpty()) {
Log.d(TAG, "任务${task.id}msg is empty, chargeSetting = $chargeSetting, statusNew = $statusNew, statusOld = $statusOld, pluggedNew = $pluggedNew, pluggedOld = $pluggedOld")
val locationSetting = Gson().fromJson(firstCondition.setting, LocationSetting::class.java)
if (locationSetting == null) {
Log.d(TAG, "任务${task.id}locationSetting is null")
continue
}
//TODO判断其他条件是否满足
//TODO判断条件是否满足
//TODO: 组装消息体 && 执行具体任务
val msg = locationInfoNew.toString()
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()
WorkManager.getInstance().enqueue(actionRequest)
}

@ -44,11 +44,13 @@
android:layout_weight="1"
android:orientation="horizontal" />
<TextView
android:id="@+id/tv_time"
<com.xuexiang.xui.widget.button.switchbutton.SwitchButton
android:id="@+id/sb_enable_task"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="5dp" />
android:paddingTop="@dimen/config_padding_5dp"
android:paddingBottom="@dimen/config_padding_5dp"
tools:ignore="TouchTargetSizeCheck" />
</LinearLayout>
@ -56,7 +58,8 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal">
android:orientation="horizontal"
tools:ignore="DisableBaselineAlignment">
<LinearLayout
android:id="@+id/layout_image"
@ -113,37 +116,59 @@
</LinearLayout>
<ImageView
android:id="@+id/iv_copy"
android:layout_width="@dimen/card_view_image_size"
android:layout_height="@dimen/card_view_image_size"
android:padding="@dimen/card_view_image_padding"
android:scaleType="fitCenter"
android:src="@drawable/ic_copy"
app:tint="@color/colorStart"
tools:ignore="ContentDescription,ImageContrastCheck" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:orientation="vertical">
<ImageView
android:id="@+id/iv_edit"
android:layout_width="@dimen/card_view_image_size"
android:layout_height="@dimen/card_view_image_size"
android:padding="@dimen/card_view_image_padding"
android:scaleType="fitCenter"
android:src="@drawable/ic_edit"
app:tint="@color/toast_info_color"
tools:ignore="ContentDescription,PrivateResource,ImageContrastCheck" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal">
<ImageView
android:id="@+id/iv_copy"
android:layout_width="28dp"
android:layout_height="28dp"
android:padding="@dimen/card_view_image_padding"
android:scaleType="fitCenter"
android:src="@drawable/ic_copy"
app:tint="@color/colorStart"
tools:ignore="ContentDescription,ImageContrastCheck" />
<ImageView
android:id="@+id/iv_edit"
android:layout_width="28dp"
android:layout_height="28dp"
android:padding="@dimen/card_view_image_padding"
android:scaleType="fitCenter"
android:src="@drawable/ic_edit"
app:tint="@color/toast_info_color"
tools:ignore="ContentDescription,PrivateResource,ImageContrastCheck" />
<ImageView
android:id="@+id/iv_delete"
android:layout_width="28dp"
android:layout_height="28dp"
android:padding="@dimen/card_view_image_padding"
android:scaleType="fitCenter"
android:src="@drawable/ic_delete"
app:tint="@color/toast_error_color"
tools:ignore="ContentDescription,PrivateResource" />
</LinearLayout>
<ImageView
android:id="@+id/iv_delete"
android:layout_width="@dimen/card_view_image_size"
android:layout_height="@dimen/card_view_image_size"
android:padding="@dimen/card_view_image_padding"
android:scaleType="fitCenter"
android:src="@drawable/ic_delete"
app:tint="@color/toast_error_color"
tools:ignore="ContentDescription,PrivateResource" />
<TextView
android:id="@+id/tv_time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="5dp" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
</com.xuexiang.xui.widget.layout.XUIFrameLayout>

@ -524,7 +524,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="5dp"
android:text="@string/seconds"
android:text="@string/meter"
android:textSize="12sp"
android:textStyle="bold" />

@ -1087,6 +1087,7 @@
<string name="ipv6">IPv6</string>
<string name="enable_location">Enable Location Function</string>
<string name="enable_location_dialog">Enable location services to use this feature. Yes or No?</string>
<string name="enable_location_tips">Used for locating the phone, {{LOCATION}} tag.</string>
<string name="accuracy">Accuracy</string>
<string name="accuracy_fine">Fine</string>
@ -1096,7 +1097,7 @@
<string name="power_requirement_low">Low</string>
<string name="power_requirement_medium">Medium</string>
<string name="power_requirement_high">High</string>
<string name="min_interval">Min Interval For Update</string>
<string name="min_interval">To Update: Min Interval</string>
<string name="min_distance">Min Distance</string>
<string name="meter">m</string>
<string name="uid">UID</string>
@ -1241,4 +1242,6 @@
<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="only_one_location_condition">Only one condition, either "To Address" or "Leave Address" can be added.</string>
<string name="current_address">Current Address: %s</string>
<string name="location_failed">Location failed. Please try again later.</string>
</resources>

@ -1088,6 +1088,7 @@
<string name="ipv6">IPv6</string>
<string name="enable_location">启用GPS定位功能</string>
<string name="enable_location_dialog">必须开启定位服务,才能使用获取!\n是否立即启用</string>
<string name="enable_location_tips">用于支持 查找手机、{{定位信息}}标签 功能</string>
<string name="accuracy">位置精度</string>
<string name="accuracy_fine">精确位置</string>
@ -1097,7 +1098,7 @@
<string name="power_requirement_low"></string>
<string name="power_requirement_medium"></string>
<string name="power_requirement_high"></string>
<string name="min_interval">位置更新最小间隔</string>
<string name="min_interval">位置更新最小间隔</string>
<string name="min_distance">最小距离</string>
<string name="meter"></string>
<string name="uid">UID</string>
@ -1235,11 +1236,13 @@
<string name="keyword_leave_address_2">则表示离开</string>
<string name="calc_type_distance_error">经纬度或距离都不能为空</string>
<string name="calc_type_address_error">地址关键字不能为空</string>
<string name="to_address_distance_description">进入以经度%s维度%s 为中心,%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_keyword_description">离开GPS地址包含 %s 关键字区域</string>
<string name="to_address_distance_description">进入以经度:%s,维度:%s为中心,%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_keyword_description">离开GPS地址包含[%s]关键字区域</string>
<string name="condition_already_exists">已经添加过该类型条件</string>
<string name="action_already_exists">已经添加过该类型动作</string>
<string name="only_one_location_condition">只能添加一个 进入地点 或 离开地址 类型条件</string>
<string name="current_address">当前地址:%s</string>
<string name="location_failed">定位失败,请稍后重试</string>
</resources>

Loading…
Cancel
Save