mirror of
https://github.com/pppscn/SmsForwarder
synced 2025-08-03 01:17:41 +08:00
新增:自动任务·快捷指令 —— 触发条件:蓝牙设备(状态变化、设备发现、连接断开) #388
This commit is contained in:
parent
e4432cb037
commit
ad79f5e445
@ -67,6 +67,11 @@
|
||||
<uses-permission
|
||||
android:name="android.permission.REBOOT"
|
||||
tools:ignore="ProtectedPermissions" />
|
||||
<uses-permission android:name="android.permission.BLUETOOTH" />
|
||||
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
|
||||
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
|
||||
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
|
||||
<uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" />
|
||||
|
||||
<application
|
||||
android:name=".App"
|
||||
@ -101,7 +106,7 @@
|
||||
android:taskAffinity=":splash"
|
||||
android:theme="@style/AppTheme.Launch.App"
|
||||
android:windowSoftInputMode="adjustPan|stateHidden"
|
||||
tools:ignore="TranslucentOrientation">
|
||||
tools:ignore="DiscouragedApi,TranslucentOrientation">
|
||||
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
@ -113,21 +118,24 @@
|
||||
android:configChanges="screenSize|keyboardHidden|orientation|keyboard"
|
||||
android:exported="true"
|
||||
android:screenOrientation="portrait"
|
||||
android:windowSoftInputMode="adjustPan|stateHidden" />
|
||||
android:windowSoftInputMode="adjustPan|stateHidden"
|
||||
tools:ignore="DiscouragedApi" />
|
||||
<activity
|
||||
android:name=".activity.ClientActivity"
|
||||
android:configChanges="screenSize|keyboardHidden|orientation|keyboard"
|
||||
android:exported="true"
|
||||
android:launchMode="singleInstance"
|
||||
android:screenOrientation="portrait"
|
||||
android:windowSoftInputMode="adjustPan|stateHidden" />
|
||||
android:windowSoftInputMode="adjustPan|stateHidden"
|
||||
tools:ignore="DiscouragedApi" />
|
||||
<activity
|
||||
android:name=".activity.TaskActivity"
|
||||
android:configChanges="screenSize|keyboardHidden|orientation|keyboard"
|
||||
android:exported="true"
|
||||
android:launchMode="singleInstance"
|
||||
android:screenOrientation="portrait"
|
||||
android:windowSoftInputMode="adjustPan|stateHidden" />
|
||||
android:windowSoftInputMode="adjustPan|stateHidden"
|
||||
tools:ignore="DiscouragedApi" />
|
||||
<!--通用浏览器-->
|
||||
<activity
|
||||
android:name=".core.webview.AgentWebActivity"
|
||||
@ -185,26 +193,30 @@
|
||||
android:configChanges="screenSize|keyboardHidden|orientation|keyboard"
|
||||
android:exported="true"
|
||||
android:screenOrientation="portrait"
|
||||
android:windowSoftInputMode="adjustPan|stateHidden" />
|
||||
android:windowSoftInputMode="adjustPan|stateHidden"
|
||||
tools:ignore="DiscouragedApi" />
|
||||
<!-- 版本更新提示-->
|
||||
<activity
|
||||
android:name=".utils.update.UpdateTipDialog"
|
||||
android:exported="true"
|
||||
android:screenOrientation="portrait"
|
||||
android:theme="@style/DialogTheme" />
|
||||
android:theme="@style/DialogTheme"
|
||||
tools:ignore="DiscouragedApi" />
|
||||
<!-- Webview拦截提示弹窗-->
|
||||
<activity
|
||||
android:name=".core.webview.WebViewInterceptDialog"
|
||||
android:exported="true"
|
||||
android:screenOrientation="portrait"
|
||||
android:theme="@style/DialogTheme" />
|
||||
android:theme="@style/DialogTheme"
|
||||
tools:ignore="DiscouragedApi" />
|
||||
<!-- applink的中转页面 -->
|
||||
<activity
|
||||
android:name=".core.XPageTransferActivity"
|
||||
android:configChanges="screenSize|keyboardHidden|orientation|keyboard"
|
||||
android:exported="true"
|
||||
android:screenOrientation="portrait"
|
||||
android:windowSoftInputMode="adjustPan|stateHidden" />
|
||||
android:windowSoftInputMode="adjustPan|stateHidden"
|
||||
tools:ignore="DiscouragedApi" />
|
||||
|
||||
<!--屏幕自适应设计图-->
|
||||
<meta-data
|
||||
@ -216,6 +228,10 @@
|
||||
android:exported="true"
|
||||
android:value="640" />
|
||||
|
||||
<service
|
||||
android:name=".service.BluetoothScanService"
|
||||
android:enabled="true"
|
||||
android:exported="false" />
|
||||
<service
|
||||
android:name=".service.ForegroundService"
|
||||
android:enabled="true" />
|
||||
@ -244,6 +260,30 @@
|
||||
<action android:name="android.intent.action.BATTERY_CHANGED" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
<receiver
|
||||
android:name=".receiver.BluetoothReceiver"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<!-- 蓝牙设备发现 -->
|
||||
<action android:name="android.bluetooth.device.action.FOUND" />
|
||||
<!-- 蓝牙扫描完成 -->
|
||||
<action android:name="android.bluetooth.adapter.action.DISCOVERY_FINISHED" />
|
||||
<!-- 蓝牙状态改变 -->
|
||||
<action android:name="android.bluetooth.adapter.action.STATE_CHANGED" />
|
||||
<!-- 蓝牙扫描模式改变 -->
|
||||
<action android:name="android.bluetooth.adapter.action.SCAN_MODE_CHANGED" />
|
||||
<!-- 本地蓝牙名称改变 -->
|
||||
<action android:name="android.bluetooth.adapter.action.LOCAL_NAME_CHANGED" />
|
||||
<!-- 蓝牙连接状态改变 -->
|
||||
<action android:name="android.bluetooth.adapter.action.CONNECTION_STATE_CHANGED" />
|
||||
<!-- 蓝牙设备配对状态改变 -->
|
||||
<action android:name="android.bluetooth.device.action.BOND_STATE_CHANGED" />
|
||||
<!-- 蓝牙设备连接 -->
|
||||
<action android:name="android.bluetooth.device.action.ACL_CONNECTED" />
|
||||
<!-- 蓝牙设备断开连接 -->
|
||||
<action android:name="android.bluetooth.device.action.ACL_DISCONNECTED" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
<receiver
|
||||
android:name=".receiver.BootCompletedReceiver"
|
||||
android:defaultToDeviceProtectedStorage="true"
|
||||
|
@ -3,6 +3,8 @@ package com.idormy.sms.forwarder
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Application
|
||||
import android.app.PendingIntent
|
||||
import android.bluetooth.BluetoothAdapter
|
||||
import android.bluetooth.BluetoothDevice
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.IntentFilter
|
||||
@ -30,12 +32,15 @@ import com.idormy.sms.forwarder.database.repository.SenderRepository
|
||||
import com.idormy.sms.forwarder.database.repository.TaskRepository
|
||||
import com.idormy.sms.forwarder.entity.SimInfo
|
||||
import com.idormy.sms.forwarder.receiver.BatteryReceiver
|
||||
import com.idormy.sms.forwarder.receiver.BluetoothReceiver
|
||||
import com.idormy.sms.forwarder.receiver.CactusReceiver
|
||||
import com.idormy.sms.forwarder.receiver.LockScreenReceiver
|
||||
import com.idormy.sms.forwarder.receiver.NetworkChangeReceiver
|
||||
import com.idormy.sms.forwarder.service.BluetoothScanService
|
||||
import com.idormy.sms.forwarder.service.ForegroundService
|
||||
import com.idormy.sms.forwarder.service.HttpServerService
|
||||
import com.idormy.sms.forwarder.service.LocationService
|
||||
import com.idormy.sms.forwarder.utils.ACTION_START
|
||||
import com.idormy.sms.forwarder.utils.AppInfo
|
||||
import com.idormy.sms.forwarder.utils.CactusSave
|
||||
import com.idormy.sms.forwarder.utils.FRONT_CHANNEL_ID
|
||||
@ -185,7 +190,7 @@ class App : Application(), CactusCallback, Configuration.Provider by Core {
|
||||
|
||||
//启动前台服务
|
||||
val foregroundServiceIntent = Intent(this, ForegroundService::class.java)
|
||||
foregroundServiceIntent.action = "START"
|
||||
foregroundServiceIntent.action = ACTION_START
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
startForegroundService(foregroundServiceIntent)
|
||||
} else {
|
||||
@ -202,7 +207,7 @@ class App : Application(), CactusCallback, Configuration.Provider by Core {
|
||||
//启动LocationService
|
||||
if (SettingUtils.enableLocation) {
|
||||
val locationServiceIntent = Intent(this, LocationService::class.java)
|
||||
locationServiceIntent.action = "START"
|
||||
locationServiceIntent.action = ACTION_START
|
||||
startService(locationServiceIntent)
|
||||
}
|
||||
|
||||
@ -211,6 +216,26 @@ class App : Application(), CactusCallback, Configuration.Provider by Core {
|
||||
val batteryFilter = IntentFilter(Intent.ACTION_BATTERY_CHANGED)
|
||||
registerReceiver(batteryReceiver, batteryFilter)
|
||||
|
||||
//监听蓝牙状态变化
|
||||
val bluetoothReceiver = BluetoothReceiver()
|
||||
val filter = IntentFilter().apply {
|
||||
addAction(BluetoothDevice.ACTION_FOUND)
|
||||
addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED)
|
||||
addAction(BluetoothAdapter.ACTION_STATE_CHANGED)
|
||||
addAction(BluetoothAdapter.ACTION_SCAN_MODE_CHANGED)
|
||||
addAction(BluetoothAdapter.ACTION_LOCAL_NAME_CHANGED)
|
||||
addAction(BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED)
|
||||
addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED)
|
||||
addAction(BluetoothDevice.ACTION_ACL_CONNECTED)
|
||||
addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED)
|
||||
}
|
||||
registerReceiver(bluetoothReceiver, filter)
|
||||
if (SettingUtils.enableBluetooth) {
|
||||
val bluetoothScanServiceIntent = Intent(this, BluetoothScanService::class.java)
|
||||
bluetoothScanServiceIntent.action = ACTION_START
|
||||
startService(bluetoothScanServiceIntent)
|
||||
}
|
||||
|
||||
//监听网络变化
|
||||
val networkReceiver = NetworkChangeReceiver()
|
||||
val networkFilter = IntentFilter().apply {
|
||||
|
@ -36,6 +36,7 @@ import com.idormy.sms.forwarder.fragment.ServerFragment
|
||||
import com.idormy.sms.forwarder.fragment.SettingsFragment
|
||||
import com.idormy.sms.forwarder.fragment.TasksFragment
|
||||
import com.idormy.sms.forwarder.service.ForegroundService
|
||||
import com.idormy.sms.forwarder.utils.ACTION_START
|
||||
import com.idormy.sms.forwarder.utils.CommonUtils.Companion.restartApplication
|
||||
import com.idormy.sms.forwarder.utils.EVENT_LOAD_APP_LIST
|
||||
import com.idormy.sms.forwarder.utils.FRPC_LIB_DOWNLOAD_URL
|
||||
@ -121,7 +122,7 @@ class MainActivity : BaseActivity<ActivityMainBinding?>(), DrawerAdapter.OnItemS
|
||||
//启动前台服务
|
||||
if (!ForegroundService.isRunning) {
|
||||
val serviceIntent = Intent(this, ForegroundService::class.java)
|
||||
serviceIntent.action = "START"
|
||||
serviceIntent.action = ACTION_START
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
startForegroundService(serviceIntent)
|
||||
} else {
|
||||
|
@ -0,0 +1,122 @@
|
||||
package com.idormy.sms.forwarder.adapter
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.bluetooth.BluetoothClass
|
||||
import android.bluetooth.BluetoothDevice
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.ImageView
|
||||
import android.widget.TextView
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.idormy.sms.forwarder.R
|
||||
|
||||
class BluetoothRecyclerAdapter(
|
||||
private val itemList: List<BluetoothDevice>,
|
||||
private var itemClickListener: ((Int) -> Unit)? = null,
|
||||
private var removeClickListener: ((Int) -> Unit)? = null,
|
||||
private var editClickListener: ((Int) -> Unit)? = null,
|
||||
) : RecyclerView.Adapter<BluetoothRecyclerAdapter.ViewHolder>() {
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
||||
val view = LayoutInflater.from(parent.context).inflate(R.layout.adapter_bluetooth_list_item, parent, false)
|
||||
return ViewHolder(view)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
||||
val item = itemList[position]
|
||||
holder.bind(item)
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int = itemList.size
|
||||
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView), View.OnClickListener {
|
||||
private val textDeviceName: TextView = itemView.findViewById(R.id.text_device_name)
|
||||
private val textDeviceAddress: TextView = itemView.findViewById(R.id.text_device_address)
|
||||
private val imageDeviceIcon: ImageView = itemView.findViewById(R.id.image_device_icon)
|
||||
private val editIcon: ImageView = itemView.findViewById(R.id.iv_edit)
|
||||
private val removeIcon: ImageView = itemView.findViewById(R.id.iv_remove)
|
||||
|
||||
init {
|
||||
if (removeClickListener == null) {
|
||||
removeIcon.visibility = View.GONE
|
||||
} else {
|
||||
removeIcon.setOnClickListener(this)
|
||||
}
|
||||
|
||||
if (editClickListener == null) {
|
||||
editIcon.visibility = View.GONE
|
||||
} else {
|
||||
editIcon.setOnClickListener(this)
|
||||
}
|
||||
|
||||
if (itemClickListener != null) {
|
||||
itemView.setOnClickListener(this)
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("MissingPermission")
|
||||
fun bind(device: BluetoothDevice) {
|
||||
// 设置设备名称和地址
|
||||
textDeviceName.text = device.name ?: "Unknown Device"
|
||||
textDeviceAddress.text = device.address
|
||||
|
||||
// 根据设备类型设置图标
|
||||
val deviceType = getDeviceType(device)
|
||||
val iconResId = when (deviceType) {
|
||||
DeviceType.CELLPHONE -> R.drawable.ic_bt_cellphone
|
||||
DeviceType.HEADPHONES -> R.drawable.ic_bt_headphones
|
||||
DeviceType.HEADSET_HFP -> R.drawable.ic_bt_headset_hfp
|
||||
DeviceType.IMAGING -> R.drawable.ic_bt_imaging
|
||||
DeviceType.LAPTOP -> R.drawable.ic_bt_laptop
|
||||
DeviceType.MISC_HID -> R.drawable.ic_bt_misc_hid
|
||||
DeviceType.NETWORK_PAN -> R.drawable.ic_bt_network_pan
|
||||
DeviceType.WRISTBAND -> R.drawable.ic_bt_wristband
|
||||
else -> R.drawable.ic_bt_bluetooth
|
||||
}
|
||||
imageDeviceIcon.setImageResource(iconResId)
|
||||
}
|
||||
|
||||
override fun onClick(v: View?) {
|
||||
val position = adapterPosition
|
||||
if (position != RecyclerView.NO_POSITION) {
|
||||
when (v?.id) {
|
||||
R.id.iv_edit -> editClickListener?.let { it(position) }
|
||||
R.id.iv_remove -> removeClickListener?.let { it(position) }
|
||||
else -> itemClickListener?.let { it(position) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("MissingPermission")
|
||||
private fun getDeviceType(device: BluetoothDevice): DeviceType {
|
||||
val deviceClass = device.bluetoothClass?.majorDeviceClass ?: BluetoothClass.Device.Major.MISC
|
||||
@Suppress("DUPLICATE_LABEL_IN_WHEN")
|
||||
return when (deviceClass) {
|
||||
BluetoothClass.Device.Major.PHONE -> DeviceType.CELLPHONE
|
||||
BluetoothClass.Device.Major.AUDIO_VIDEO -> DeviceType.HEADPHONES
|
||||
BluetoothClass.Device.Major.PERIPHERAL -> DeviceType.HEADSET_HFP
|
||||
BluetoothClass.Device.Major.IMAGING -> DeviceType.IMAGING
|
||||
BluetoothClass.Device.Major.COMPUTER -> DeviceType.LAPTOP
|
||||
BluetoothClass.Device.Major.PERIPHERAL -> DeviceType.MISC_HID
|
||||
BluetoothClass.Device.Major.NETWORKING -> DeviceType.NETWORK_PAN
|
||||
BluetoothClass.Device.Major.WEARABLE -> DeviceType.WRISTBAND
|
||||
else -> DeviceType.UNKNOWN
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum class DeviceType {
|
||||
CELLPHONE,
|
||||
HEADPHONES,
|
||||
HEADSET_HFP,
|
||||
IMAGING,
|
||||
LAPTOP,
|
||||
MISC_HID,
|
||||
NETWORK_PAN,
|
||||
WRISTBAND,
|
||||
UNKNOWN
|
||||
}
|
||||
}
|
@ -0,0 +1,88 @@
|
||||
package com.idormy.sms.forwarder.entity.condition
|
||||
|
||||
import android.bluetooth.BluetoothAdapter
|
||||
import android.bluetooth.BluetoothDevice
|
||||
import com.idormy.sms.forwarder.App
|
||||
import com.idormy.sms.forwarder.R
|
||||
import com.xuexiang.xutil.resource.ResUtils.getString
|
||||
import java.io.Serializable
|
||||
|
||||
data class BluetoothSetting(
|
||||
var description: String = "", //描述
|
||||
var action: String = BluetoothAdapter.ACTION_STATE_CHANGED, //事件
|
||||
var state: Int = BluetoothAdapter.STATE_ON, //蓝牙状态
|
||||
var result: Int = 1, //搜索结果:1-已发现,0-未发现
|
||||
var device: String = "", //设备MAC地址
|
||||
) : Serializable {
|
||||
|
||||
constructor(actionCheckId: Int, stateCheckId: Int, resultCheckId: Int, deviceAddress: String) : this() {
|
||||
device = deviceAddress
|
||||
action = when (actionCheckId) {
|
||||
R.id.rb_action_discovery_finished -> BluetoothAdapter.ACTION_DISCOVERY_FINISHED
|
||||
R.id.rb_action_acl_connected -> BluetoothDevice.ACTION_ACL_CONNECTED
|
||||
R.id.rb_action_acl_disconnected -> BluetoothDevice.ACTION_ACL_DISCONNECTED
|
||||
else -> BluetoothAdapter.ACTION_STATE_CHANGED
|
||||
}
|
||||
state = when (stateCheckId) {
|
||||
R.id.rb_state_off -> BluetoothAdapter.STATE_OFF
|
||||
else -> BluetoothAdapter.STATE_ON
|
||||
}
|
||||
result = when (resultCheckId) {
|
||||
R.id.rb_undiscovered -> 0
|
||||
else -> 1
|
||||
}
|
||||
val sb = StringBuilder()
|
||||
|
||||
if (action == BluetoothAdapter.ACTION_STATE_CHANGED) {
|
||||
sb.append(getString(R.string.bluetooth_state_changed)).append(", ").append(getString(R.string.specified_state)).append(": ")
|
||||
if (state == BluetoothAdapter.STATE_ON) {
|
||||
sb.append(getString(R.string.state_on))
|
||||
} else {
|
||||
sb.append(getString(R.string.state_off))
|
||||
}
|
||||
} else if (action == BluetoothAdapter.ACTION_DISCOVERY_FINISHED) {
|
||||
sb.append(getString(R.string.bluetooth_discovery_finished)).append(", ")
|
||||
if (result == 1) {
|
||||
sb.append(getString(R.string.discovered))
|
||||
} else {
|
||||
sb.append(getString(R.string.undiscovered))
|
||||
}
|
||||
val blank = if (App.isNeedSpaceBetweenWords) " " else ""
|
||||
sb.append(blank).append(getString(R.string.specified_device)).append(": ").append(device)
|
||||
} else {
|
||||
if (action == BluetoothDevice.ACTION_ACL_CONNECTED) {
|
||||
sb.append(getString(R.string.bluetooth_acl_connected))
|
||||
} else if (action == BluetoothDevice.ACTION_ACL_DISCONNECTED) {
|
||||
sb.append(getString(R.string.bluetooth_acl_disconnected))
|
||||
}
|
||||
sb.append(", ").append(getString(R.string.specified_device)).append(": ").append(device)
|
||||
}
|
||||
|
||||
description = sb.toString()
|
||||
}
|
||||
|
||||
fun getActionCheckId(): Int {
|
||||
return when (action) {
|
||||
BluetoothAdapter.ACTION_STATE_CHANGED -> R.id.rb_action_state_changed
|
||||
BluetoothAdapter.ACTION_DISCOVERY_FINISHED -> R.id.rb_action_discovery_finished
|
||||
BluetoothDevice.ACTION_ACL_CONNECTED -> R.id.rb_action_acl_connected
|
||||
BluetoothDevice.ACTION_ACL_DISCONNECTED -> R.id.rb_action_acl_disconnected
|
||||
else -> R.id.rb_action_state_changed
|
||||
}
|
||||
}
|
||||
|
||||
fun getStateCheckId(): Int {
|
||||
return when (state) {
|
||||
BluetoothAdapter.STATE_ON -> R.id.rb_state_on
|
||||
BluetoothAdapter.STATE_OFF -> R.id.rb_state_off
|
||||
else -> R.id.rb_state_on
|
||||
}
|
||||
}
|
||||
|
||||
fun getResultCheckId(): Int {
|
||||
return when (result) {
|
||||
0 -> R.id.rb_undiscovered
|
||||
else -> R.id.rb_discovered
|
||||
}
|
||||
}
|
||||
}
|
@ -18,6 +18,7 @@ import com.idormy.sms.forwarder.database.viewmodel.BaseViewModelFactory
|
||||
import com.idormy.sms.forwarder.database.viewmodel.FrpcViewModel
|
||||
import com.idormy.sms.forwarder.databinding.FragmentFrpcsBinding
|
||||
import com.idormy.sms.forwarder.service.ForegroundService
|
||||
import com.idormy.sms.forwarder.utils.ACTION_START
|
||||
import com.idormy.sms.forwarder.utils.EVENT_FRPC_DELETE_CONFIG
|
||||
import com.idormy.sms.forwarder.utils.EVENT_FRPC_RUNNING_ERROR
|
||||
import com.idormy.sms.forwarder.utils.EVENT_FRPC_RUNNING_SUCCESS
|
||||
@ -153,7 +154,7 @@ class FrpcFragment : BaseFragment<FragmentFrpcsBinding?>(), FrpcPagingAdapter.On
|
||||
|
||||
if (!ForegroundService.isRunning) {
|
||||
val serviceIntent = Intent(requireContext(), ForegroundService::class.java)
|
||||
serviceIntent.action = "START"
|
||||
serviceIntent.action = ACTION_START
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
requireContext().startForegroundService(serviceIntent)
|
||||
} else {
|
||||
|
@ -21,7 +21,15 @@ import com.idormy.sms.forwarder.core.BaseFragment
|
||||
import com.idormy.sms.forwarder.databinding.FragmentServerBinding
|
||||
import com.idormy.sms.forwarder.service.HttpServerService
|
||||
import com.idormy.sms.forwarder.service.LocationService
|
||||
import com.idormy.sms.forwarder.utils.*
|
||||
import com.idormy.sms.forwarder.utils.ACTION_RESTART
|
||||
import com.idormy.sms.forwarder.utils.Base64
|
||||
import com.idormy.sms.forwarder.utils.HTTP_SERVER_PORT
|
||||
import com.idormy.sms.forwarder.utils.HttpServerUtils
|
||||
import com.idormy.sms.forwarder.utils.Log
|
||||
import com.idormy.sms.forwarder.utils.RandomUtils
|
||||
import com.idormy.sms.forwarder.utils.SM4Crypt
|
||||
import com.idormy.sms.forwarder.utils.SettingUtils
|
||||
import com.idormy.sms.forwarder.utils.XToastUtils
|
||||
import com.xuexiang.xaop.annotation.SingleClick
|
||||
import com.xuexiang.xpage.annotation.Page
|
||||
import com.xuexiang.xui.widget.actionbar.TitleBar
|
||||
@ -256,7 +264,7 @@ class ServerFragment : BaseFragment<FragmentServerBinding?>(), View.OnClickListe
|
||||
}
|
||||
//重启前台服务,启动/停止定位服务
|
||||
val serviceIntent = Intent(requireContext(), LocationService::class.java)
|
||||
serviceIntent.action = "RESTART"
|
||||
serviceIntent.action = ACTION_RESTART
|
||||
requireContext().startService(serviceIntent)
|
||||
}
|
||||
|
||||
|
@ -42,12 +42,19 @@ import com.idormy.sms.forwarder.core.BaseFragment
|
||||
import com.idormy.sms.forwarder.databinding.FragmentSettingsBinding
|
||||
import com.idormy.sms.forwarder.entity.SimInfo
|
||||
import com.idormy.sms.forwarder.receiver.BootCompletedReceiver
|
||||
import com.idormy.sms.forwarder.service.BluetoothScanService
|
||||
import com.idormy.sms.forwarder.service.ForegroundService
|
||||
import com.idormy.sms.forwarder.service.LocationService
|
||||
import com.idormy.sms.forwarder.utils.ACTION_RESTART
|
||||
import com.idormy.sms.forwarder.utils.ACTION_START
|
||||
import com.idormy.sms.forwarder.utils.ACTION_STOP
|
||||
import com.idormy.sms.forwarder.utils.ACTION_UPDATE_NOTIFICATION
|
||||
import com.idormy.sms.forwarder.utils.AppUtils.getAppPackageName
|
||||
import com.idormy.sms.forwarder.utils.BluetoothUtils
|
||||
import com.idormy.sms.forwarder.utils.CommonUtils
|
||||
import com.idormy.sms.forwarder.utils.DataProvider
|
||||
import com.idormy.sms.forwarder.utils.EVENT_LOAD_APP_LIST
|
||||
import com.idormy.sms.forwarder.utils.EXTRA_UPDATE_NOTIFICATION
|
||||
import com.idormy.sms.forwarder.utils.KeepAliveUtils
|
||||
import com.idormy.sms.forwarder.utils.LocationUtils
|
||||
import com.idormy.sms.forwarder.utils.Log
|
||||
@ -124,7 +131,9 @@ class SettingsFragment : BaseFragment<FragmentSettingsBinding?>(), View.OnClickL
|
||||
//转发应用通知
|
||||
switchEnableAppNotify(binding!!.sbEnableAppNotify, binding!!.scbCancelAppNotify, binding!!.scbNotUserPresent)
|
||||
|
||||
//启用GPS定位功能
|
||||
//发现蓝牙设备服务
|
||||
switchEnableBluetooth(binding!!.sbEnableBluetooth, binding!!.layoutBluetoothSetting, binding!!.xsbScanInterval, binding!!.scbIgnoreAnonymous)
|
||||
//GPS定位功能
|
||||
switchEnableLocation(binding!!.sbEnableLocation, binding!!.layoutLocationSetting, binding!!.rgAccuracy, binding!!.rgPowerRequirement, binding!!.xsbMinInterval, binding!!.xsbMinDistance)
|
||||
//短信指令
|
||||
switchEnableSmsCommand(binding!!.sbEnableSmsCommand, binding!!.etSafePhone)
|
||||
@ -309,10 +318,10 @@ class SettingsFragment : BaseFragment<FragmentSettingsBinding?>(), View.OnClickL
|
||||
}
|
||||
|
||||
R.id.btn_export_log -> {
|
||||
// 申请储存权限
|
||||
XXPermissions.with(this)
|
||||
//.permission(*Permission.Group.STORAGE)
|
||||
.permission(Permission.MANAGE_EXTERNAL_STORAGE).request(object : OnPermissionCallback {
|
||||
// 申请储存权限
|
||||
.permission(Permission.MANAGE_EXTERNAL_STORAGE)
|
||||
.request(object : OnPermissionCallback {
|
||||
@SuppressLint("SetTextI18n")
|
||||
override fun onGranted(permissions: List<String>, all: Boolean) {
|
||||
try {
|
||||
@ -352,7 +361,6 @@ class SettingsFragment : BaseFragment<FragmentSettingsBinding?>(), View.OnClickL
|
||||
sbEnableSms.setOnCheckedChangeListener { _: CompoundButton?, isChecked: Boolean ->
|
||||
SettingUtils.enableSms = isChecked
|
||||
if (isChecked) {
|
||||
//检查权限是否获取
|
||||
XXPermissions.with(this)
|
||||
// 接收 WAP 推送消息
|
||||
.permission(Permission.RECEIVE_WAP_PUSH)
|
||||
@ -408,7 +416,6 @@ class SettingsFragment : BaseFragment<FragmentSettingsBinding?>(), View.OnClickL
|
||||
}
|
||||
SettingUtils.enablePhone = isChecked
|
||||
if (isChecked) {
|
||||
//检查权限是否获取
|
||||
XXPermissions.with(this)
|
||||
// 读取电话状态
|
||||
.permission(Permission.READ_PHONE_STATE)
|
||||
@ -499,7 +506,6 @@ class SettingsFragment : BaseFragment<FragmentSettingsBinding?>(), View.OnClickL
|
||||
binding!!.layoutOptionalAction.visibility = if (isChecked) View.VISIBLE else View.GONE
|
||||
SettingUtils.enableAppNotify = isChecked
|
||||
if (isChecked) {
|
||||
//检查权限是否获取
|
||||
XXPermissions.with(this)
|
||||
.permission(Permission.BIND_NOTIFICATION_LISTENER_SERVICE)
|
||||
.request(OnPermissionCallback { permissions, allGranted ->
|
||||
@ -531,7 +537,85 @@ class SettingsFragment : BaseFragment<FragmentSettingsBinding?>(), View.OnClickL
|
||||
}
|
||||
}
|
||||
|
||||
//启用定位功能
|
||||
//发现蓝牙设备服务
|
||||
private fun switchEnableBluetooth(@SuppressLint("UseSwitchCompatOrMaterialCode") sbEnableBluetooth: SwitchButton, layoutBluetoothSetting: LinearLayout, xsbScanInterval: XSeekBar, scbIgnoreAnonymous: SmoothCheckBox) {
|
||||
sbEnableBluetooth.setOnCheckedChangeListener { _: CompoundButton?, isChecked: Boolean ->
|
||||
SettingUtils.enableBluetooth = isChecked
|
||||
layoutBluetoothSetting.visibility = if (isChecked) View.VISIBLE else View.GONE
|
||||
if (isChecked) {
|
||||
XXPermissions.with(this)
|
||||
.permission(Permission.BLUETOOTH_SCAN)
|
||||
.permission(Permission.BLUETOOTH_CONNECT)
|
||||
.permission(Permission.BLUETOOTH_ADVERTISE)
|
||||
.permission(Permission.ACCESS_FINE_LOCATION)
|
||||
.request(object : OnPermissionCallback {
|
||||
override fun onGranted(permissions: List<String>, all: Boolean) {
|
||||
Log.d(TAG, "onGranted: permissions=$permissions, all=$all")
|
||||
if (!all) {
|
||||
XToastUtils.warning(getString(R.string.enable_bluetooth) + ": " + getString(R.string.toast_granted_part))
|
||||
}
|
||||
restartBluetoothService(ACTION_START)
|
||||
}
|
||||
|
||||
override fun onDenied(permissions: List<String>, never: Boolean) {
|
||||
Log.e(TAG, "onDenied: permissions=$permissions, never=$never")
|
||||
if (never) {
|
||||
XToastUtils.error(getString(R.string.enable_bluetooth) + ": " + getString(R.string.toast_denied_never))
|
||||
// 如果是被永久拒绝就跳转到应用权限系统设置页面
|
||||
XXPermissions.startPermissionActivity(requireContext(), permissions)
|
||||
} else {
|
||||
XToastUtils.error(getString(R.string.enable_bluetooth) + ": " + getString(R.string.toast_denied))
|
||||
}
|
||||
SettingUtils.enableBluetooth = false
|
||||
sbEnableBluetooth.isChecked = false
|
||||
restartBluetoothService(ACTION_STOP)
|
||||
}
|
||||
})
|
||||
} else {
|
||||
restartBluetoothService(ACTION_STOP)
|
||||
}
|
||||
}
|
||||
val isEnable = SettingUtils.enableBluetooth
|
||||
sbEnableBluetooth.isChecked = isEnable
|
||||
layoutBluetoothSetting.visibility = if (isEnable) View.VISIBLE else View.GONE
|
||||
|
||||
//扫描蓝牙设备间隔
|
||||
xsbScanInterval.setDefaultValue((SettingUtils.bluetoothScanInterval / 1000).toInt())
|
||||
xsbScanInterval.setOnSeekBarListener { _: XSeekBar?, newValue: Int ->
|
||||
if (newValue * 1000L != SettingUtils.bluetoothScanInterval) {
|
||||
SettingUtils.bluetoothScanInterval = newValue * 1000L
|
||||
restartBluetoothService()
|
||||
}
|
||||
}
|
||||
|
||||
//是否忽略匿名设备
|
||||
scbIgnoreAnonymous.isChecked = SettingUtils.bluetoothIgnoreAnonymous
|
||||
scbIgnoreAnonymous.setOnCheckedChangeListener { _: SmoothCheckBox, isChecked: Boolean ->
|
||||
SettingUtils.bluetoothIgnoreAnonymous = isChecked
|
||||
restartBluetoothService()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//重启蓝牙扫描服务
|
||||
private fun restartBluetoothService(action: String = ACTION_RESTART) {
|
||||
if (!initViewsFinished) return
|
||||
Log.d(TAG, "restartBluetoothService, action: $action")
|
||||
val serviceIntent = Intent(requireContext(), BluetoothScanService::class.java)
|
||||
//如果定位功能已启用,但是系统定位功能不可用,则关闭定位功能
|
||||
if (SettingUtils.enableBluetooth && (!BluetoothUtils.isBluetoothEnabled() || !BluetoothUtils.hasBluetoothCapability(App.context))) {
|
||||
XToastUtils.error(getString(R.string.toast_location_not_enabled))
|
||||
SettingUtils.enableBluetooth = false
|
||||
binding!!.sbEnableBluetooth.isChecked = false
|
||||
binding!!.layoutBluetoothSetting.visibility = View.GONE
|
||||
serviceIntent.action = ACTION_STOP
|
||||
} else {
|
||||
serviceIntent.action = action
|
||||
}
|
||||
requireContext().startService(serviceIntent)
|
||||
}
|
||||
|
||||
//GPS定位服务
|
||||
private fun switchEnableLocation(@SuppressLint("UseSwitchCompatOrMaterialCode") sbEnableLocation: SwitchButton, layoutLocationSetting: LinearLayout, rgAccuracy: RadioGroup, rgPowerRequirement: RadioGroup, xsbMinInterval: XSeekBar, xsbMinDistance: XSeekBar) {
|
||||
sbEnableLocation.setOnCheckedChangeListener { _: CompoundButton?, isChecked: Boolean ->
|
||||
SettingUtils.enableLocation = isChecked
|
||||
@ -547,7 +631,7 @@ class SettingsFragment : BaseFragment<FragmentSettingsBinding?>(), View.OnClickL
|
||||
if (!all) {
|
||||
XToastUtils.warning(getString(R.string.enable_location) + ": " + getString(R.string.toast_granted_part))
|
||||
}
|
||||
restartLocationService("START")
|
||||
restartLocationService(ACTION_START)
|
||||
}
|
||||
|
||||
override fun onDenied(permissions: List<String>, never: Boolean) {
|
||||
@ -561,11 +645,11 @@ class SettingsFragment : BaseFragment<FragmentSettingsBinding?>(), View.OnClickL
|
||||
}
|
||||
SettingUtils.enableLocation = false
|
||||
sbEnableLocation.isChecked = false
|
||||
restartLocationService("STOP")
|
||||
restartLocationService(ACTION_STOP)
|
||||
}
|
||||
})
|
||||
} else {
|
||||
restartLocationService("STOP")
|
||||
restartLocationService(ACTION_STOP)
|
||||
}
|
||||
}
|
||||
val isEnable = SettingUtils.enableLocation
|
||||
@ -632,7 +716,7 @@ class SettingsFragment : BaseFragment<FragmentSettingsBinding?>(), View.OnClickL
|
||||
}
|
||||
|
||||
//重启定位服务
|
||||
private fun restartLocationService(action: String = "RESTART") {
|
||||
private fun restartLocationService(action: String = ACTION_RESTART) {
|
||||
if (!initViewsFinished) return
|
||||
Log.d(TAG, "restartLocationService, action: $action")
|
||||
val serviceIntent = Intent(requireContext(), LocationService::class.java)
|
||||
@ -642,7 +726,7 @@ class SettingsFragment : BaseFragment<FragmentSettingsBinding?>(), View.OnClickL
|
||||
SettingUtils.enableLocation = false
|
||||
binding!!.sbEnableLocation.isChecked = false
|
||||
binding!!.layoutLocationSetting.visibility = View.GONE
|
||||
serviceIntent.action = "STOP"
|
||||
serviceIntent.action = ACTION_STOP
|
||||
} else {
|
||||
serviceIntent.action = action
|
||||
}
|
||||
@ -656,7 +740,6 @@ class SettingsFragment : BaseFragment<FragmentSettingsBinding?>(), View.OnClickL
|
||||
SettingUtils.enableSmsCommand = isChecked
|
||||
etSafePhone.visibility = if (isChecked) View.VISIBLE else View.GONE
|
||||
if (isChecked) {
|
||||
//检查权限是否获取
|
||||
XXPermissions.with(this)
|
||||
// 系统设置
|
||||
.permission(Permission.WRITE_SETTINGS)
|
||||
@ -962,8 +1045,8 @@ class SettingsFragment : BaseFragment<FragmentSettingsBinding?>(), View.OnClickL
|
||||
val notifyContent = etNotifyContent.text.toString().trim()
|
||||
SettingUtils.notifyContent = notifyContent
|
||||
val updateIntent = Intent(context, ForegroundService::class.java)
|
||||
updateIntent.action = "UPDATE_NOTIFICATION"
|
||||
updateIntent.putExtra("UPDATED_CONTENT", notifyContent)
|
||||
updateIntent.action = ACTION_UPDATE_NOTIFICATION
|
||||
updateIntent.putExtra(EXTRA_UPDATE_NOTIFICATION, notifyContent)
|
||||
context?.let { ContextCompat.startForegroundService(it, updateIntent) }
|
||||
}
|
||||
})
|
||||
|
@ -82,56 +82,56 @@ class TasksEditFragment : BaseFragment<FragmentTasksEditBinding?>(), View.OnClic
|
||||
PageInfo(
|
||||
getString(R.string.task_cron),
|
||||
"com.idormy.sms.forwarder.fragment.condition.CronFragment",
|
||||
"{\"\":\"\"}",
|
||||
"",
|
||||
CoreAnim.slide,
|
||||
R.drawable.auto_task_icon_custom_time,
|
||||
),
|
||||
PageInfo(
|
||||
getString(R.string.task_to_address),
|
||||
"com.idormy.sms.forwarder.fragment.condition.ToAddressFragment",
|
||||
"{\"\":\"\"}",
|
||||
"",
|
||||
CoreAnim.slide,
|
||||
R.drawable.auto_task_icon_to_address,
|
||||
),
|
||||
PageInfo(
|
||||
getString(R.string.task_leave_address),
|
||||
"com.idormy.sms.forwarder.fragment.condition.LeaveAddressFragment",
|
||||
"{\"\":\"\"}",
|
||||
"",
|
||||
CoreAnim.slide,
|
||||
R.drawable.auto_task_icon_leave_address,
|
||||
),
|
||||
PageInfo(
|
||||
getString(R.string.task_network),
|
||||
"com.idormy.sms.forwarder.fragment.condition.NetworkFragment",
|
||||
"{\"\":\"\"}",
|
||||
"",
|
||||
CoreAnim.slide,
|
||||
R.drawable.auto_task_icon_network
|
||||
),
|
||||
PageInfo(
|
||||
getString(R.string.task_sim),
|
||||
"com.idormy.sms.forwarder.fragment.condition.SimFragment",
|
||||
"{\"\":\"\"}",
|
||||
"",
|
||||
CoreAnim.slide,
|
||||
R.drawable.auto_task_icon_sim
|
||||
),
|
||||
PageInfo(
|
||||
getString(R.string.task_battery),
|
||||
"com.idormy.sms.forwarder.fragment.condition.BatteryFragment",
|
||||
"{\"\":\"\"}",
|
||||
"",
|
||||
CoreAnim.slide,
|
||||
R.drawable.auto_task_icon_battery
|
||||
),
|
||||
PageInfo(
|
||||
getString(R.string.task_charge),
|
||||
"com.idormy.sms.forwarder.fragment.condition.ChargeFragment",
|
||||
"{\"\":\"\"}",
|
||||
"",
|
||||
CoreAnim.slide,
|
||||
R.drawable.auto_task_icon_charge
|
||||
),
|
||||
PageInfo(
|
||||
getString(R.string.task_lock_screen),
|
||||
"com.idormy.sms.forwarder.fragment.condition.LockScreenFragment",
|
||||
"{\"\":\"\"}",
|
||||
"",
|
||||
CoreAnim.slide,
|
||||
R.drawable.auto_task_icon_lock_screen
|
||||
),
|
||||
@ -156,83 +156,90 @@ class TasksEditFragment : BaseFragment<FragmentTasksEditBinding?>(), View.OnClic
|
||||
CoreAnim.slide,
|
||||
R.drawable.auto_task_icon_start_activity
|
||||
),
|
||||
PageInfo(
|
||||
getString(R.string.task_bluetooth),
|
||||
"com.idormy.sms.forwarder.fragment.condition.BluetoothFragment",
|
||||
"",
|
||||
CoreAnim.slide,
|
||||
R.drawable.auto_task_icon_bluetooth
|
||||
),
|
||||
)
|
||||
|
||||
private var TASK_ACTION_FRAGMENT_LIST = listOf(
|
||||
PageInfo(
|
||||
getString(R.string.task_sendsms),
|
||||
"com.idormy.sms.forwarder.fragment.action.SendSmsFragment",
|
||||
"{\"\":\"\"}",
|
||||
"",
|
||||
CoreAnim.slide,
|
||||
R.drawable.auto_task_icon_sms
|
||||
),
|
||||
PageInfo(
|
||||
getString(R.string.task_notification),
|
||||
"com.idormy.sms.forwarder.fragment.action.NotificationFragment",
|
||||
"{\"\":\"\"}",
|
||||
"",
|
||||
CoreAnim.slide,
|
||||
R.drawable.auto_task_icon_notification,
|
||||
),
|
||||
PageInfo(
|
||||
getString(R.string.task_cleaner),
|
||||
"com.idormy.sms.forwarder.fragment.action.CleanerFragment",
|
||||
"{\"\":\"\"}",
|
||||
"",
|
||||
CoreAnim.slide,
|
||||
R.drawable.auto_task_icon_cleaner
|
||||
),
|
||||
PageInfo(
|
||||
getString(R.string.task_settings),
|
||||
"com.idormy.sms.forwarder.fragment.action.SettingsFragment",
|
||||
"{\"\":\"\"}",
|
||||
"",
|
||||
CoreAnim.slide,
|
||||
R.drawable.auto_task_icon_settings
|
||||
),
|
||||
PageInfo(
|
||||
getString(R.string.task_frpc),
|
||||
"com.idormy.sms.forwarder.fragment.action.FrpcFragment",
|
||||
"{\"\":\"\"}",
|
||||
"",
|
||||
CoreAnim.slide,
|
||||
R.drawable.auto_task_icon_frpc
|
||||
),
|
||||
PageInfo(
|
||||
getString(R.string.task_http_server),
|
||||
"com.idormy.sms.forwarder.fragment.action.HttpServerFragment",
|
||||
"{\"\":\"\"}",
|
||||
"",
|
||||
CoreAnim.slide,
|
||||
R.drawable.auto_task_icon_http_server
|
||||
),
|
||||
PageInfo(
|
||||
getString(R.string.task_rule),
|
||||
"com.idormy.sms.forwarder.fragment.action.RuleFragment",
|
||||
"{\"\":\"\"}",
|
||||
"",
|
||||
CoreAnim.slide,
|
||||
R.drawable.auto_task_icon_rule
|
||||
),
|
||||
PageInfo(
|
||||
getString(R.string.task_sender),
|
||||
"com.idormy.sms.forwarder.fragment.action.SenderFragment",
|
||||
"{\"\":\"\"}",
|
||||
"",
|
||||
CoreAnim.slide,
|
||||
R.drawable.auto_task_icon_sender
|
||||
),
|
||||
PageInfo(
|
||||
getString(R.string.task_alarm),
|
||||
"com.idormy.sms.forwarder.fragment.action.AlarmFragment",
|
||||
"{\"\":\"\"}",
|
||||
"",
|
||||
CoreAnim.slide,
|
||||
R.drawable.auto_task_icon_alarm
|
||||
),
|
||||
PageInfo(
|
||||
getString(R.string.task_resend),
|
||||
"com.idormy.sms.forwarder.fragment.action.ResendFragment",
|
||||
"{\"\":\"\"}",
|
||||
"",
|
||||
CoreAnim.slide,
|
||||
R.drawable.auto_task_icon_resend
|
||||
),
|
||||
PageInfo(
|
||||
getString(R.string.task_task),
|
||||
"com.idormy.sms.forwarder.fragment.action.TaskActionFragment",
|
||||
"{\"\":\"\"}",
|
||||
"",
|
||||
CoreAnim.slide,
|
||||
R.drawable.auto_task_icon_task
|
||||
),
|
||||
@ -556,7 +563,7 @@ class TasksEditFragment : BaseFragment<FragmentTasksEditBinding?>(), View.OnClic
|
||||
.negativeText(R.string.lab_no).onPositive { _: MaterialDialog?, _: DialogAction? ->
|
||||
SettingUtils.enableLocation = true
|
||||
val serviceIntent = Intent(requireContext(), LocationService::class.java)
|
||||
serviceIntent.action = "START"
|
||||
serviceIntent.action = ACTION_START
|
||||
requireContext().startService(serviceIntent)
|
||||
}.show()
|
||||
return
|
||||
|
@ -164,7 +164,6 @@ class SettingsFragment : BaseFragment<FragmentTasksActionSettingsBinding?>(), Vi
|
||||
|
||||
binding!!.sbEnableSms.setOnCheckedChangeListener { _: CompoundButton?, isChecked: Boolean ->
|
||||
if (isChecked) {
|
||||
//检查权限是否获取
|
||||
XXPermissions.with(this)
|
||||
// 接收 WAP 推送消息
|
||||
.permission(Permission.RECEIVE_WAP_PUSH)
|
||||
@ -175,7 +174,8 @@ class SettingsFragment : BaseFragment<FragmentTasksActionSettingsBinding?>(), Vi
|
||||
// 发送短信
|
||||
//.permission(Permission.SEND_SMS)
|
||||
// 读取短信
|
||||
.permission(Permission.READ_SMS).request(object : OnPermissionCallback {
|
||||
.permission(Permission.READ_SMS)
|
||||
.request(object : OnPermissionCallback {
|
||||
override fun onGranted(permissions: List<String>, all: Boolean) {
|
||||
if (all) {
|
||||
XToastUtils.info(R.string.toast_granted_all)
|
||||
@ -200,7 +200,6 @@ class SettingsFragment : BaseFragment<FragmentTasksActionSettingsBinding?>(), Vi
|
||||
|
||||
binding!!.sbEnablePhone.setOnCheckedChangeListener { _: CompoundButton?, isChecked: Boolean ->
|
||||
if (isChecked) {
|
||||
//检查权限是否获取
|
||||
XXPermissions.with(this)
|
||||
// 读取电话状态
|
||||
.permission(Permission.READ_PHONE_STATE)
|
||||
@ -209,7 +208,8 @@ class SettingsFragment : BaseFragment<FragmentTasksActionSettingsBinding?>(), Vi
|
||||
// 读取通话记录
|
||||
.permission(Permission.READ_CALL_LOG)
|
||||
// 读取联系人
|
||||
.permission(Permission.READ_CONTACTS).request(object : OnPermissionCallback {
|
||||
.permission(Permission.READ_CONTACTS)
|
||||
.request(object : OnPermissionCallback {
|
||||
override fun onGranted(permissions: List<String>, all: Boolean) {
|
||||
if (all) {
|
||||
XToastUtils.info(R.string.toast_granted_all)
|
||||
@ -234,8 +234,9 @@ class SettingsFragment : BaseFragment<FragmentTasksActionSettingsBinding?>(), Vi
|
||||
|
||||
binding!!.sbEnableAppNotify.setOnCheckedChangeListener { _: CompoundButton?, isChecked: Boolean ->
|
||||
if (isChecked) {
|
||||
//检查权限是否获取
|
||||
XXPermissions.with(this).permission(Permission.BIND_NOTIFICATION_LISTENER_SERVICE).request(OnPermissionCallback { _, allGranted ->
|
||||
XXPermissions.with(this)
|
||||
.permission(Permission.BIND_NOTIFICATION_LISTENER_SERVICE)
|
||||
.request(OnPermissionCallback { _, allGranted ->
|
||||
if (!allGranted) {
|
||||
binding!!.sbEnableAppNotify.isChecked = false
|
||||
XToastUtils.error(R.string.tips_notification_listener)
|
||||
@ -250,7 +251,11 @@ class SettingsFragment : BaseFragment<FragmentTasksActionSettingsBinding?>(), Vi
|
||||
|
||||
binding!!.sbEnableLocation.setOnCheckedChangeListener { _: CompoundButton?, isChecked: Boolean ->
|
||||
if (isChecked) {
|
||||
XXPermissions.with(this).permission(Permission.ACCESS_COARSE_LOCATION).permission(Permission.ACCESS_FINE_LOCATION).permission(Permission.ACCESS_BACKGROUND_LOCATION).request(object : OnPermissionCallback {
|
||||
XXPermissions.with(this)
|
||||
.permission(Permission.ACCESS_COARSE_LOCATION)
|
||||
.permission(Permission.ACCESS_FINE_LOCATION)
|
||||
.permission(Permission.ACCESS_BACKGROUND_LOCATION)
|
||||
.request(object : OnPermissionCallback {
|
||||
override fun onGranted(permissions: List<String>, all: Boolean) {
|
||||
}
|
||||
|
||||
@ -296,7 +301,6 @@ class SettingsFragment : BaseFragment<FragmentTasksActionSettingsBinding?>(), Vi
|
||||
|
||||
binding!!.sbEnableSmsCommand.setOnCheckedChangeListener { _: CompoundButton?, isChecked: Boolean ->
|
||||
if (isChecked) {
|
||||
//检查权限是否获取
|
||||
XXPermissions.with(this)
|
||||
// 系统设置
|
||||
.permission(Permission.WRITE_SETTINGS)
|
||||
@ -305,7 +309,8 @@ class SettingsFragment : BaseFragment<FragmentTasksActionSettingsBinding?>(), Vi
|
||||
// 发送短信
|
||||
.permission(Permission.SEND_SMS)
|
||||
// 读取短信
|
||||
.permission(Permission.READ_SMS).request(object : OnPermissionCallback {
|
||||
.permission(Permission.READ_SMS)
|
||||
.request(object : OnPermissionCallback {
|
||||
override fun onGranted(permissions: List<String>, all: Boolean) {
|
||||
if (all) {
|
||||
XToastUtils.info(R.string.toast_granted_all)
|
||||
|
@ -0,0 +1,325 @@
|
||||
package com.idormy.sms.forwarder.fragment.condition
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.bluetooth.BluetoothAdapter
|
||||
import android.bluetooth.BluetoothDevice
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.IntentFilter
|
||||
import android.text.Editable
|
||||
import android.text.TextWatcher
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.google.gson.Gson
|
||||
import com.hjq.permissions.OnPermissionCallback
|
||||
import com.hjq.permissions.Permission
|
||||
import com.hjq.permissions.XXPermissions
|
||||
import com.idormy.sms.forwarder.R
|
||||
import com.idormy.sms.forwarder.adapter.BluetoothRecyclerAdapter
|
||||
import com.idormy.sms.forwarder.core.BaseFragment
|
||||
import com.idormy.sms.forwarder.databinding.FragmentTasksConditionBluetoothBinding
|
||||
import com.idormy.sms.forwarder.entity.condition.BluetoothSetting
|
||||
import com.idormy.sms.forwarder.service.BluetoothScanService
|
||||
import com.idormy.sms.forwarder.utils.ACTION_START
|
||||
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.Log
|
||||
import com.idormy.sms.forwarder.utils.SettingUtils
|
||||
import com.idormy.sms.forwarder.utils.TASK_CONDITION_BLUETOOTH
|
||||
import com.idormy.sms.forwarder.utils.XToastUtils
|
||||
import com.xuexiang.xaop.annotation.SingleClick
|
||||
import com.xuexiang.xpage.annotation.Page
|
||||
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 = "Bluetooth")
|
||||
@Suppress("PrivatePropertyName", "SameParameterValue", "DEPRECATION")
|
||||
class BluetoothFragment : BaseFragment<FragmentTasksConditionBluetoothBinding?>(), View.OnClickListener {
|
||||
|
||||
private val TAG: String = BluetoothFragment::class.java.simpleName
|
||||
private var titleBar: TitleBar? = null
|
||||
private var mCountDownHelper: CountDownButtonHelper? = null
|
||||
|
||||
private lateinit var bluetoothAdapter: BluetoothAdapter
|
||||
private lateinit var bluetoothRecyclerAdapter: BluetoothRecyclerAdapter
|
||||
private var discoveredDevices: MutableList<BluetoothDevice> = mutableListOf()
|
||||
private val bluetoothReceiver = object : BroadcastReceiver() {
|
||||
@SuppressLint("MissingPermission", "NotifyDataSetChanged")
|
||||
override fun onReceive(context: Context?, intent: Intent?) {
|
||||
val action: String? = intent?.action
|
||||
|
||||
when (action) {
|
||||
BluetoothDevice.ACTION_FOUND -> {
|
||||
val device: BluetoothDevice? = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE)
|
||||
device?.let {
|
||||
Log.d(TAG, "Discovered device: ${it.name} - ${it.address}")
|
||||
if (!discoveredDevices.contains(it)) {
|
||||
discoveredDevices.add(it)
|
||||
bluetoothRecyclerAdapter.notifyDataSetChanged()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BluetoothAdapter.ACTION_DISCOVERY_FINISHED -> {
|
||||
Log.d(TAG, "Bluetooth scan finished, discoveredDevices: $discoveredDevices")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@JvmField
|
||||
@AutoWired(name = KEY_EVENT_DATA_CONDITION)
|
||||
var eventData: String? = null
|
||||
|
||||
override fun initArgs() {
|
||||
XRouter.getInstance().inject(this)
|
||||
}
|
||||
|
||||
override fun viewBindingInflate(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup,
|
||||
): FragmentTasksConditionBluetoothBinding {
|
||||
return FragmentTasksConditionBluetoothBinding.inflate(inflater, container, false)
|
||||
}
|
||||
|
||||
override fun initTitle(): TitleBar? {
|
||||
titleBar = super.initTitle()!!.setImmersive(false).setTitle(R.string.task_bluetooth)
|
||||
return titleBar
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化控件
|
||||
*/
|
||||
override fun initViews() {
|
||||
//测试按钮增加倒计时,避免重复点击
|
||||
mCountDownHelper = CountDownButtonHelper(binding!!.btnStartDiscovery, 12)
|
||||
mCountDownHelper!!.setOnCountDownListener(object : CountDownButtonHelper.OnCountDownListener {
|
||||
override fun onCountDown(time: Int) {
|
||||
binding!!.btnStartDiscovery.text = String.format(getString(R.string.seconds_n), time)
|
||||
}
|
||||
|
||||
override fun onFinished() {
|
||||
requireActivity().unregisterReceiver(bluetoothReceiver)
|
||||
binding!!.btnStartDiscovery.text = getString(R.string.start_discovery)
|
||||
}
|
||||
})
|
||||
|
||||
binding!!.rgBluetoothAction.setOnCheckedChangeListener { _, checkedId ->
|
||||
Log.d(TAG, "rgBluetoothState checkedId:$checkedId")
|
||||
when (checkedId) {
|
||||
R.id.rb_action_state_changed -> {
|
||||
binding!!.layoutBluetoothState.visibility = View.VISIBLE
|
||||
binding!!.layoutDiscoveryFinished.visibility = View.GONE
|
||||
binding!!.layoutDeviceAddress.visibility = View.GONE
|
||||
}
|
||||
|
||||
R.id.rb_action_discovery_finished -> {
|
||||
binding!!.layoutBluetoothState.visibility = View.GONE
|
||||
binding!!.layoutDiscoveryFinished.visibility = View.VISIBLE
|
||||
binding!!.layoutDeviceAddress.visibility = View.VISIBLE
|
||||
}
|
||||
|
||||
else -> {
|
||||
binding!!.layoutBluetoothState.visibility = View.GONE
|
||||
binding!!.layoutDiscoveryFinished.visibility = View.GONE
|
||||
binding!!.layoutDeviceAddress.visibility = View.VISIBLE
|
||||
}
|
||||
}
|
||||
checkSetting(true)
|
||||
}
|
||||
|
||||
Log.d(TAG, "initViews eventData:$eventData")
|
||||
if (eventData != null) {
|
||||
val settingVo = Gson().fromJson(eventData, BluetoothSetting::class.java)
|
||||
Log.d(TAG, "initViews settingVo:$settingVo")
|
||||
binding!!.tvDescription.text = settingVo.description
|
||||
binding!!.rgBluetoothAction.check(settingVo.getActionCheckId())
|
||||
binding!!.rgBluetoothState.check(settingVo.getStateCheckId())
|
||||
binding!!.rgDiscoveryResult.check(settingVo.getResultCheckId())
|
||||
binding!!.etDeviceAddress.setText(settingVo.device)
|
||||
} else {
|
||||
binding!!.rgBluetoothAction.check(R.id.rb_action_state_changed)
|
||||
binding!!.rgBluetoothState.check(R.id.rb_state_on)
|
||||
binding!!.rgDiscoveryResult.check(R.id.rb_discovered)
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("SetTextI18n")
|
||||
override fun initListeners() {
|
||||
binding!!.btnStartDiscovery.setOnClickListener(this)
|
||||
binding!!.btnDel.setOnClickListener(this)
|
||||
binding!!.btnSave.setOnClickListener(this)
|
||||
binding!!.rgBluetoothState.setOnCheckedChangeListener { _, _ ->
|
||||
checkSetting(true)
|
||||
}
|
||||
binding!!.rgDiscoveryResult.setOnCheckedChangeListener { _, _ ->
|
||||
checkSetting(true)
|
||||
}
|
||||
binding!!.etDeviceAddress.addTextChangedListener(object : TextWatcher {
|
||||
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
|
||||
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {}
|
||||
override fun afterTextChanged(s: Editable?) {
|
||||
checkSetting(true)
|
||||
}
|
||||
})
|
||||
|
||||
binding!!.recyclerDevices.layoutManager = LinearLayoutManager(requireContext())
|
||||
bluetoothRecyclerAdapter = BluetoothRecyclerAdapter(discoveredDevices, { position ->
|
||||
val device = discoveredDevices[position]
|
||||
binding!!.etDeviceAddress.setText(device.address)
|
||||
})
|
||||
binding!!.recyclerDevices.adapter = bluetoothRecyclerAdapter
|
||||
|
||||
bluetoothAdapter = BluetoothAdapter.getDefaultAdapter()
|
||||
@Suppress("SENSELESS_COMPARISON")
|
||||
if (bluetoothAdapter == null) {
|
||||
XToastUtils.error(getString(R.string.bluetooth_not_supported))
|
||||
return
|
||||
}
|
||||
|
||||
// 启动蓝牙搜索
|
||||
// startBluetoothDiscovery()
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
if (mCountDownHelper != null) mCountDownHelper!!.recycle()
|
||||
if (bluetoothReceiver.isOrderedBroadcast) {
|
||||
requireActivity().unregisterReceiver(bluetoothReceiver)
|
||||
}
|
||||
super.onDestroyView()
|
||||
}
|
||||
|
||||
@SingleClick
|
||||
override fun onClick(v: View) {
|
||||
try {
|
||||
when (v.id) {
|
||||
|
||||
R.id.btn_start_discovery -> {
|
||||
if (!SettingUtils.enableBluetooth) {
|
||||
MaterialDialog.Builder(requireContext())
|
||||
.iconRes(R.drawable.auto_task_icon_location)
|
||||
.title(R.string.enable_bluetooth)
|
||||
.content(R.string.enable_bluetooth_dialog)
|
||||
.cancelable(false)
|
||||
.positiveText(R.string.lab_yes)
|
||||
.negativeText(R.string.lab_no)
|
||||
.onPositive { _: MaterialDialog?, _: DialogAction? ->
|
||||
XXPermissions.with(this)
|
||||
.permission(Permission.BLUETOOTH_SCAN)
|
||||
.permission(Permission.BLUETOOTH_CONNECT)
|
||||
.permission(Permission.BLUETOOTH_ADVERTISE)
|
||||
.permission(Permission.ACCESS_FINE_LOCATION)
|
||||
.request(object : OnPermissionCallback {
|
||||
override fun onGranted(permissions: List<String>, all: Boolean) {
|
||||
startBluetoothDiscovery()
|
||||
Log.d(TAG, "onGranted: permissions=$permissions, all=$all")
|
||||
if (!all) {
|
||||
XToastUtils.warning(getString(R.string.toast_granted_part))
|
||||
}
|
||||
SettingUtils.enableBluetooth = true
|
||||
val serviceIntent = Intent(requireContext(), BluetoothScanService::class.java)
|
||||
serviceIntent.action = ACTION_START
|
||||
requireContext().startService(serviceIntent)
|
||||
}
|
||||
|
||||
override fun onDenied(permissions: List<String>, never: Boolean) {
|
||||
Log.e(TAG, "onDenied: permissions=$permissions, never=$never")
|
||||
if (never) {
|
||||
XToastUtils.error(getString(R.string.toast_denied_never))
|
||||
XXPermissions.startPermissionActivity(requireContext(), permissions)
|
||||
} else {
|
||||
XToastUtils.error(getString(R.string.toast_denied))
|
||||
}
|
||||
}
|
||||
})
|
||||
}.show()
|
||||
return
|
||||
}
|
||||
|
||||
startBluetoothDiscovery()
|
||||
return
|
||||
}
|
||||
|
||||
R.id.btn_del -> {
|
||||
popToBack()
|
||||
return
|
||||
}
|
||||
|
||||
R.id.btn_save -> {
|
||||
val settingVo = checkSetting()
|
||||
val intent = Intent()
|
||||
intent.putExtra(KEY_BACK_DESCRIPTION_CONDITION, settingVo.description)
|
||||
intent.putExtra(KEY_BACK_DATA_CONDITION, Gson().toJson(settingVo))
|
||||
setFragmentResult(TASK_CONDITION_BLUETOOTH, intent)
|
||||
popToBack()
|
||||
return
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
XToastUtils.error(e.message.toString(), 30000)
|
||||
e.printStackTrace()
|
||||
Log.e(TAG, "onClick error:$e")
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("MissingPermission", "NotifyDataSetChanged")
|
||||
private fun startBluetoothDiscovery() {
|
||||
try {
|
||||
mCountDownHelper?.start()
|
||||
|
||||
if (bluetoothAdapter.isDiscovering) {
|
||||
bluetoothAdapter.cancelDiscovery()
|
||||
}
|
||||
|
||||
// 注册广播接收器
|
||||
val filter = IntentFilter().apply {
|
||||
addAction(BluetoothDevice.ACTION_FOUND)
|
||||
addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED)
|
||||
}
|
||||
requireActivity().registerReceiver(bluetoothReceiver, filter)
|
||||
|
||||
discoveredDevices.clear()
|
||||
bluetoothRecyclerAdapter.notifyDataSetChanged()
|
||||
bluetoothAdapter.startDiscovery()
|
||||
} catch (e: Exception) {
|
||||
mCountDownHelper?.finish()
|
||||
XToastUtils.error(e.message.toString(), 30000)
|
||||
Log.e(TAG, "startBluetoothDiscovery error:$e")
|
||||
}
|
||||
}
|
||||
|
||||
//检查设置
|
||||
private fun checkSetting(updateView: Boolean = false): BluetoothSetting {
|
||||
val actionCheckId = binding!!.rgBluetoothAction.checkedRadioButtonId
|
||||
val deviceAddress = binding!!.etDeviceAddress.text.toString().trim()
|
||||
if (actionCheckId != R.id.rb_action_state_changed &&
|
||||
(deviceAddress.isEmpty() || !BluetoothAdapter.checkBluetoothAddress(deviceAddress))
|
||||
) {
|
||||
if (updateView) {
|
||||
binding!!.etDeviceAddress.error = getString(R.string.mac_error)
|
||||
} else {
|
||||
throw Exception(getString(R.string.invalid_bluetooth_mac_address))
|
||||
}
|
||||
} else {
|
||||
binding!!.etDeviceAddress.error = null
|
||||
}
|
||||
|
||||
val stateCheckId = binding!!.rgBluetoothState.checkedRadioButtonId
|
||||
val resultCheckId = binding!!.rgDiscoveryResult.checkedRadioButtonId
|
||||
val settingVo = BluetoothSetting(actionCheckId, stateCheckId, resultCheckId, deviceAddress)
|
||||
if (updateView) {
|
||||
binding!!.tvDescription.text = settingVo.description
|
||||
}
|
||||
|
||||
return settingVo
|
||||
}
|
||||
}
|
@ -14,6 +14,7 @@ import com.idormy.sms.forwarder.core.BaseFragment
|
||||
import com.idormy.sms.forwarder.databinding.FragmentTasksConditionLeaveAddressBinding
|
||||
import com.idormy.sms.forwarder.entity.condition.LocationSetting
|
||||
import com.idormy.sms.forwarder.service.LocationService
|
||||
import com.idormy.sms.forwarder.utils.ACTION_START
|
||||
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
|
||||
@ -172,10 +173,17 @@ class LeaveAddressFragment : BaseFragment<FragmentTasksConditionLeaveAddressBind
|
||||
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? ->
|
||||
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"
|
||||
serviceIntent.action = ACTION_START
|
||||
requireContext().startService(serviceIntent)
|
||||
}.show()
|
||||
return
|
||||
|
@ -14,6 +14,7 @@ import com.idormy.sms.forwarder.core.BaseFragment
|
||||
import com.idormy.sms.forwarder.databinding.FragmentTasksConditionToAddressBinding
|
||||
import com.idormy.sms.forwarder.entity.condition.LocationSetting
|
||||
import com.idormy.sms.forwarder.service.LocationService
|
||||
import com.idormy.sms.forwarder.utils.ACTION_START
|
||||
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
|
||||
@ -175,7 +176,7 @@ class ToAddressFragment : BaseFragment<FragmentTasksConditionToAddressBinding?>(
|
||||
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"
|
||||
serviceIntent.action = ACTION_START
|
||||
requireContext().startService(serviceIntent)
|
||||
}.show()
|
||||
return
|
||||
|
@ -0,0 +1,192 @@
|
||||
package com.idormy.sms.forwarder.receiver
|
||||
|
||||
import android.Manifest
|
||||
import android.annotation.SuppressLint
|
||||
import android.bluetooth.BluetoothAdapter
|
||||
import android.bluetooth.BluetoothDevice
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.os.Handler
|
||||
import androidx.core.app.ActivityCompat
|
||||
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.service.BluetoothScanService
|
||||
import com.idormy.sms.forwarder.utils.ACTION_RESTART
|
||||
import com.idormy.sms.forwarder.utils.ACTION_START
|
||||
import com.idormy.sms.forwarder.utils.ACTION_STOP
|
||||
import com.idormy.sms.forwarder.utils.Log
|
||||
import com.idormy.sms.forwarder.utils.SettingUtils
|
||||
import com.idormy.sms.forwarder.utils.TASK_CONDITION_BLUETOOTH
|
||||
import com.idormy.sms.forwarder.utils.TaskWorker
|
||||
import com.idormy.sms.forwarder.utils.task.TaskUtils
|
||||
import com.idormy.sms.forwarder.workers.BluetoothWorker
|
||||
|
||||
@Suppress("PrivatePropertyName", "DEPRECATION")
|
||||
@SuppressLint("MissingPermission")
|
||||
class BluetoothReceiver : BroadcastReceiver() {
|
||||
|
||||
private val TAG: String = BluetoothReceiver::class.java.simpleName
|
||||
private val handler = Handler()
|
||||
|
||||
override fun onReceive(context: Context?, intent: Intent?) {
|
||||
if (context == null || intent == null) return
|
||||
|
||||
when (val action = intent.action) {
|
||||
// 发现设备
|
||||
BluetoothDevice.ACTION_FOUND -> {
|
||||
val device = intent.getParcelableExtra<BluetoothDevice>(BluetoothDevice.EXTRA_DEVICE)
|
||||
device?.let {
|
||||
if (ActivityCompat.checkSelfPermission(App.context, Manifest.permission.BLUETOOTH_CONNECT) != PackageManager.PERMISSION_GRANTED) return
|
||||
if (SettingUtils.bluetoothIgnoreAnonymous && it.name.isNullOrEmpty()) return
|
||||
|
||||
//TODO: 实测这里一台设备会收到两次广播
|
||||
Log.d(TAG, "Discovered device: ${it.name} - ${it.address}")
|
||||
val discoveredDevices = TaskUtils.discoveredDevices
|
||||
discoveredDevices[it.address] = it.name ?: ""
|
||||
TaskUtils.discoveredDevices = discoveredDevices
|
||||
}
|
||||
}
|
||||
|
||||
// 扫描完成
|
||||
BluetoothAdapter.ACTION_DISCOVERY_FINISHED -> {
|
||||
//TODO: 放在这里去判断是否已经发现某个设备(避免 ACTION_FOUND 重复广播)
|
||||
Log.d(TAG, "Bluetooth scan finished, discoveredDevices: ${TaskUtils.discoveredDevices}")
|
||||
if (TaskUtils.discoveredDevices.isNotEmpty()) {
|
||||
handleWorkRequest(context, action, Gson().toJson(TaskUtils.discoveredDevices))
|
||||
}
|
||||
|
||||
restartBluetoothService(ACTION_STOP)
|
||||
if (SettingUtils.enableBluetooth) {
|
||||
Log.d(TAG, "Bluetooth scan finished, restart in ${SettingUtils.bluetoothScanInterval}ms")
|
||||
handler.postDelayed({
|
||||
restartBluetoothService(ACTION_START)
|
||||
}, SettingUtils.bluetoothScanInterval)
|
||||
}
|
||||
}
|
||||
|
||||
// 蓝牙状态变化
|
||||
BluetoothAdapter.ACTION_STATE_CHANGED -> {
|
||||
val state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR)
|
||||
handleBluetoothStateChanged(state)
|
||||
handleWorkRequest(context, action, state.toString())
|
||||
}
|
||||
|
||||
// 蓝牙扫描模式变化
|
||||
BluetoothAdapter.ACTION_SCAN_MODE_CHANGED -> {
|
||||
if (SettingUtils.enableBluetooth) {
|
||||
restartBluetoothService()
|
||||
}
|
||||
}
|
||||
|
||||
// 本地蓝牙名称变化
|
||||
BluetoothAdapter.ACTION_LOCAL_NAME_CHANGED -> {
|
||||
}
|
||||
|
||||
// 蓝牙连接状态变化
|
||||
BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED -> {
|
||||
}
|
||||
|
||||
// 蓝牙设备的配对状态变化
|
||||
BluetoothDevice.ACTION_BOND_STATE_CHANGED -> {
|
||||
}
|
||||
|
||||
// 蓝牙设备连接
|
||||
BluetoothDevice.ACTION_ACL_CONNECTED -> {
|
||||
val device = intent.getParcelableExtra<BluetoothDevice>(BluetoothDevice.EXTRA_DEVICE)
|
||||
if (device != null) {
|
||||
Log.d(TAG, "Connected device: ${device.name} - ${device.address}")
|
||||
TaskUtils.connectedDevices[device.address] = device.name
|
||||
handleWorkRequest(context, action, Gson().toJson(mutableMapOf(device.address to device.name)))
|
||||
}
|
||||
}
|
||||
|
||||
// 蓝牙设备断开连接
|
||||
BluetoothDevice.ACTION_ACL_DISCONNECTED -> {
|
||||
val device = intent.getParcelableExtra<BluetoothDevice>(BluetoothDevice.EXTRA_DEVICE)
|
||||
if (device != null) {
|
||||
Log.d(TAG, "Disconnected device: ${device.name} - ${device.address}")
|
||||
TaskUtils.connectedDevices.remove(device.address)
|
||||
handleWorkRequest(context, action, Gson().toJson(mutableMapOf(device.address to device.name)))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 处理蓝牙状态变化
|
||||
private fun handleBluetoothStateChanged(state: Int) {
|
||||
when (state) {
|
||||
// 蓝牙已关闭
|
||||
BluetoothAdapter.STATE_OFF -> {
|
||||
Log.d(TAG, "BluetoothAdapter.STATE_OFF")
|
||||
TaskUtils.bluetoothState = state
|
||||
// 停止扫描 & 删除任何挂起的延迟扫描任务
|
||||
restartBluetoothService(ACTION_STOP)
|
||||
handler.removeCallbacksAndMessages(null)
|
||||
}
|
||||
|
||||
// 蓝牙已打开
|
||||
BluetoothAdapter.STATE_ON -> {
|
||||
Log.d(TAG, "BluetoothAdapter.STATE_ON")
|
||||
TaskUtils.bluetoothState = state
|
||||
if (SettingUtils.enableBluetooth) {
|
||||
restartBluetoothService(ACTION_START)
|
||||
}
|
||||
}
|
||||
|
||||
// 蓝牙正在打开
|
||||
BluetoothAdapter.STATE_TURNING_ON -> {
|
||||
Log.d(TAG, "BluetoothAdapter.STATE_TURNING_ON")
|
||||
}
|
||||
|
||||
// 蓝牙正在关闭
|
||||
BluetoothAdapter.STATE_TURNING_OFF -> {
|
||||
Log.d(TAG, "BluetoothAdapter.STATE_TURNING_OFF")
|
||||
}
|
||||
|
||||
// 蓝牙正在连接
|
||||
BluetoothAdapter.STATE_CONNECTING -> {
|
||||
Log.d(TAG, "BluetoothAdapter.STATE_CONNECTING")
|
||||
}
|
||||
|
||||
// 蓝牙已连接
|
||||
BluetoothAdapter.STATE_CONNECTED -> {
|
||||
Log.d(TAG, "BluetoothAdapter.STATE_CONNECTED")
|
||||
}
|
||||
|
||||
// 蓝牙正在断开连接
|
||||
BluetoothAdapter.STATE_DISCONNECTING -> {
|
||||
Log.d(TAG, "BluetoothAdapter.STATE_DISCONNECTING")
|
||||
}
|
||||
|
||||
// 蓝牙已断开连接
|
||||
BluetoothAdapter.STATE_DISCONNECTED -> {
|
||||
Log.d(TAG, "BluetoothAdapter.STATE_DISCONNECTED")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//重启蓝牙扫描服务
|
||||
private fun restartBluetoothService(action: String = ACTION_RESTART) {
|
||||
Log.d(TAG, "restartBluetoothService, action: $action")
|
||||
val serviceIntent = Intent(App.context, BluetoothScanService::class.java)
|
||||
serviceIntent.action = action
|
||||
App.context.startService(serviceIntent)
|
||||
}
|
||||
|
||||
private fun handleWorkRequest(context: Context, action: String, msg: String) {
|
||||
val request = OneTimeWorkRequestBuilder<BluetoothWorker>()
|
||||
.setInputData(
|
||||
workDataOf(
|
||||
TaskWorker.CONDITION_TYPE to TASK_CONDITION_BLUETOOTH,
|
||||
TaskWorker.ACTION to action,
|
||||
TaskWorker.MSG to msg,
|
||||
)
|
||||
).build()
|
||||
WorkManager.getInstance(context).enqueue(request)
|
||||
}
|
||||
}
|
@ -0,0 +1,70 @@
|
||||
package com.idormy.sms.forwarder.service
|
||||
|
||||
import android.Manifest
|
||||
import android.app.Service
|
||||
import android.bluetooth.BluetoothAdapter
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.os.IBinder
|
||||
import androidx.core.app.ActivityCompat
|
||||
import com.idormy.sms.forwarder.utils.ACTION_RESTART
|
||||
import com.idormy.sms.forwarder.utils.ACTION_START
|
||||
import com.idormy.sms.forwarder.utils.ACTION_STOP
|
||||
import com.idormy.sms.forwarder.utils.Log
|
||||
|
||||
@Suppress("PrivatePropertyName", "DEPRECATION")
|
||||
class BluetoothScanService : Service() {
|
||||
|
||||
private val TAG: String = BluetoothScanService::class.java.simpleName
|
||||
private val bluetoothAdapter: BluetoothAdapter? = BluetoothAdapter.getDefaultAdapter()
|
||||
|
||||
companion object {
|
||||
var isRunning = false
|
||||
}
|
||||
|
||||
override fun onBind(p0: Intent?): IBinder? {
|
||||
return null
|
||||
}
|
||||
|
||||
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
||||
if (intent == null) return START_NOT_STICKY
|
||||
Log.i(TAG, "onStartCommand: ${intent.action}")
|
||||
|
||||
when (intent.action) {
|
||||
ACTION_START -> startDiscovery()
|
||||
ACTION_STOP -> stopDiscovery()
|
||||
ACTION_RESTART -> {
|
||||
stopDiscovery()
|
||||
startDiscovery()
|
||||
}
|
||||
}
|
||||
return START_NOT_STICKY
|
||||
}
|
||||
|
||||
// 开始扫描蓝牙设备
|
||||
private fun startDiscovery() {
|
||||
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.BLUETOOTH_SCAN) != PackageManager.PERMISSION_GRANTED) {
|
||||
return
|
||||
}
|
||||
if (isRunning) return
|
||||
bluetoothAdapter?.startDiscovery()
|
||||
isRunning = true
|
||||
}
|
||||
|
||||
// 停止蓝牙扫描
|
||||
private fun stopDiscovery() {
|
||||
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.BLUETOOTH_SCAN) != PackageManager.PERMISSION_GRANTED) {
|
||||
return
|
||||
}
|
||||
if (!isRunning) return
|
||||
bluetoothAdapter?.cancelDiscovery()
|
||||
isRunning = false
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
isRunning = false
|
||||
stopDiscovery()
|
||||
}
|
||||
|
||||
}
|
@ -1,7 +1,11 @@
|
||||
package com.idormy.sms.forwarder.service
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.*
|
||||
import android.app.Notification
|
||||
import android.app.NotificationChannel
|
||||
import android.app.NotificationManager
|
||||
import android.app.PendingIntent
|
||||
import android.app.Service
|
||||
import android.content.Intent
|
||||
import android.graphics.BitmapFactory
|
||||
import android.graphics.Color
|
||||
@ -20,7 +24,22 @@ import com.idormy.sms.forwarder.R
|
||||
import com.idormy.sms.forwarder.activity.MainActivity
|
||||
import com.idormy.sms.forwarder.core.Core
|
||||
import com.idormy.sms.forwarder.entity.action.AlarmSetting
|
||||
import com.idormy.sms.forwarder.utils.*
|
||||
import com.idormy.sms.forwarder.utils.ACTION_START
|
||||
import com.idormy.sms.forwarder.utils.ACTION_STOP
|
||||
import com.idormy.sms.forwarder.utils.ACTION_STOP_ALARM
|
||||
import com.idormy.sms.forwarder.utils.ACTION_UPDATE_NOTIFICATION
|
||||
import com.idormy.sms.forwarder.utils.CommonUtils
|
||||
import com.idormy.sms.forwarder.utils.EVENT_ALARM_ACTION
|
||||
import com.idormy.sms.forwarder.utils.EVENT_FRPC_RUNNING_ERROR
|
||||
import com.idormy.sms.forwarder.utils.EVENT_FRPC_RUNNING_SUCCESS
|
||||
import com.idormy.sms.forwarder.utils.EXTRA_UPDATE_NOTIFICATION
|
||||
import com.idormy.sms.forwarder.utils.FRONT_CHANNEL_ID
|
||||
import com.idormy.sms.forwarder.utils.FRONT_CHANNEL_NAME
|
||||
import com.idormy.sms.forwarder.utils.FRONT_NOTIFY_ID
|
||||
import com.idormy.sms.forwarder.utils.INTENT_FRPC_APPLY_FILE
|
||||
import com.idormy.sms.forwarder.utils.Log
|
||||
import com.idormy.sms.forwarder.utils.SettingUtils
|
||||
import com.idormy.sms.forwarder.utils.TASK_CONDITION_CRON
|
||||
import com.idormy.sms.forwarder.utils.task.CronJobScheduler
|
||||
import com.idormy.sms.forwarder.workers.LoadAppListWorker
|
||||
import com.jeremyliao.liveeventbus.LiveEventBus
|
||||
@ -167,20 +186,20 @@ class ForegroundService : Service() {
|
||||
|
||||
if (intent != null) {
|
||||
when (intent.action) {
|
||||
"START" -> {
|
||||
ACTION_START -> {
|
||||
startForegroundService()
|
||||
}
|
||||
|
||||
"STOP" -> {
|
||||
ACTION_STOP -> {
|
||||
stopForegroundService()
|
||||
}
|
||||
|
||||
"UPDATE_NOTIFICATION" -> {
|
||||
val updatedContent = intent.getStringExtra("UPDATED_CONTENT")
|
||||
ACTION_UPDATE_NOTIFICATION -> {
|
||||
val updatedContent = intent.getStringExtra(EXTRA_UPDATE_NOTIFICATION)
|
||||
updateNotification(updatedContent ?: "")
|
||||
}
|
||||
|
||||
"STOP_ALARM" -> {
|
||||
ACTION_STOP_ALARM -> {
|
||||
alarmPlayer?.release()
|
||||
alarmPlayer = null
|
||||
updateNotification(SettingUtils.notifyContent)
|
||||
@ -307,7 +326,7 @@ class ForegroundService : Service() {
|
||||
// 添加停止按钮(可选)
|
||||
if (showStopButton) {
|
||||
val stopIntent = Intent(this, ForegroundService::class.java).apply {
|
||||
action = "STOP_ALARM"
|
||||
action = ACTION_STOP_ALARM
|
||||
}
|
||||
val stopPendingIntent = PendingIntent.getService(this, 0, stopIntent, flags)
|
||||
builder.addAction(R.drawable.ic_stop, getString(R.string.stop), stopPendingIntent)
|
||||
|
@ -15,6 +15,9 @@ 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.ACTION_RESTART
|
||||
import com.idormy.sms.forwarder.utils.ACTION_START
|
||||
import com.idormy.sms.forwarder.utils.ACTION_STOP
|
||||
import com.idormy.sms.forwarder.utils.HttpServerUtils
|
||||
import com.idormy.sms.forwarder.utils.LocationUtils
|
||||
import com.idormy.sms.forwarder.utils.Log
|
||||
@ -64,19 +67,14 @@ class LocationService : Service() {
|
||||
|
||||
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
||||
super.onStartCommand(intent, flags, startId)
|
||||
|
||||
if (intent == null) return START_NOT_STICKY
|
||||
|
||||
Log.i(TAG, "onStartCommand: ${intent.action}")
|
||||
|
||||
if (intent.action == "START" && !isRunning) {
|
||||
startService()
|
||||
} else if (intent.action == "STOP" && isRunning) {
|
||||
stopService()
|
||||
} else if (intent.action == "RESTART") {
|
||||
restartLocation()
|
||||
when {
|
||||
intent.action == ACTION_START && !isRunning -> startService()
|
||||
intent.action == ACTION_STOP && isRunning -> stopService()
|
||||
intent.action == ACTION_RESTART -> restartLocation()
|
||||
}
|
||||
|
||||
return START_STICKY
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,38 @@
|
||||
package com.idormy.sms.forwarder.utils
|
||||
|
||||
import android.bluetooth.BluetoothAdapter
|
||||
import android.content.Context
|
||||
import android.content.pm.PackageManager
|
||||
import androidx.core.content.ContextCompat
|
||||
|
||||
@Suppress("DEPRECATION", "MemberVisibilityCanBePrivate")
|
||||
object BluetoothUtils {
|
||||
|
||||
/**
|
||||
* 检查应用是否具有蓝牙权限
|
||||
*/
|
||||
fun hasBluetoothPermission(context: Context): Boolean {
|
||||
return ContextCompat.checkSelfPermission(context, android.Manifest.permission.BLUETOOTH) == PackageManager.PERMISSION_GRANTED
|
||||
&& ContextCompat.checkSelfPermission(context, android.Manifest.permission.BLUETOOTH_ADMIN) == PackageManager.PERMISSION_GRANTED
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查蓝牙是否已启用
|
||||
*/
|
||||
fun isBluetoothEnabled(): Boolean {
|
||||
val bluetoothAdapter = BluetoothAdapter.getDefaultAdapter()
|
||||
return bluetoothAdapter != null && bluetoothAdapter.isEnabled
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查设备是否支持蓝牙功能
|
||||
*/
|
||||
fun hasBluetoothCapability(context: Context): Boolean {
|
||||
if (!hasBluetoothPermission(context)) {
|
||||
Log.e("BluetoothUtils", "hasBluetoothCapability: no bluetooth permission")
|
||||
return false
|
||||
}
|
||||
|
||||
return context.packageManager.hasSystemFeature(PackageManager.FEATURE_BLUETOOTH)
|
||||
}
|
||||
}
|
@ -19,6 +19,14 @@ object TaskWorker {
|
||||
const val ACTION = "action"
|
||||
}
|
||||
|
||||
//服务相关
|
||||
const val ACTION_START = "START"
|
||||
const val ACTION_STOP = "STOP"
|
||||
const val ACTION_RESTART = "RESTART"
|
||||
const val ACTION_STOP_ALARM = "STOP_ALARM"
|
||||
const val ACTION_UPDATE_NOTIFICATION = "UPDATE_NOTIFICATION"
|
||||
const val EXTRA_UPDATE_NOTIFICATION = "EXTRA_UPDATE_NOTIFICATION"
|
||||
|
||||
//初始化相关
|
||||
const val AUTO_CHECK_UPDATE = "auto_check_update"
|
||||
const val JOIN_PREVIEW_PROGRAM = "join_preview_program"
|
||||
@ -84,6 +92,10 @@ const val SP_LOCATION_POWER_REQUIREMENT = "location_power_requirement"
|
||||
const val SP_LOCATION_MIN_INTERVAL = "location_min_interval_time"
|
||||
const val SP_LOCATION_MIN_DISTANCE = "location_min_distance"
|
||||
|
||||
const val SP_BLUETOOTH = "enable_bluetooth"
|
||||
const val SP_BLUETOOTH_SCAN_INTERVAL = "bluetooth_scan_interval"
|
||||
const val SP_BLUETOOTH_IGNORE_ANONYMOUS = "bluetooth_ignore_anonymous"
|
||||
|
||||
const val SP_ENABLE_CACTUS = "enable_cactus"
|
||||
const val CACTUS_TIMER = "cactus_timer"
|
||||
const val CACTUS_LAST_TIMER = "cactus_last_timer"
|
||||
@ -239,6 +251,7 @@ const val TASK_CONDITION_LOCK_SCREEN = 1007
|
||||
const val TASK_CONDITION_SMS = 1008
|
||||
const val TASK_CONDITION_CALL = 1009
|
||||
const val TASK_CONDITION_APP = 1010
|
||||
const val TASK_CONDITION_BLUETOOTH = 1011
|
||||
|
||||
//注意:TASK_ACTION_XXX 枚举值 等于 TASK_ACTION_FRAGMENT_LIST 索引加上 KEY_BACK_CODE_ACTION,不可改变
|
||||
const val TASK_ACTION_SENDSMS = 2000
|
||||
@ -268,6 +281,9 @@ const val SP_SIM_STATE = "sim_state"
|
||||
const val SP_LOCATION_INFO_OLD = "location_info_old"
|
||||
const val SP_LOCATION_INFO_NEW = "location_info_new"
|
||||
const val SP_LOCK_SCREEN_ACTION = "lock_screen_action"
|
||||
const val SP_CONNECTED_DEVICE = "connected_device"
|
||||
const val SP_DISCOVERED_DEVICES = "discovered_devices"
|
||||
const val SP_BLUETOOTH_STATE = "bluetooth_state"
|
||||
|
||||
//SIM卡已准备就绪时,延迟5秒(给够搜索信号时间)才执行任务
|
||||
const val DELAY_TIME_AFTER_SIM_READY = 5000L
|
||||
|
@ -148,6 +148,15 @@ class SettingUtils private constructor() {
|
||||
|
||||
//是否跟随系统语言
|
||||
//var isFlowSystemLanguage: Boolean by SharedPreference(SP_IS_FLOW_SYSTEM_LANGUAGE, false)
|
||||
|
||||
//是否启用发现蓝牙设备服务
|
||||
var enableBluetooth: Boolean by SharedPreference(SP_BLUETOOTH, false)
|
||||
|
||||
//扫描蓝牙设备间隔
|
||||
var bluetoothScanInterval: Long by SharedPreference(SP_BLUETOOTH_SCAN_INTERVAL, 10000L)
|
||||
|
||||
//是否忽略匿名设备
|
||||
var bluetoothIgnoreAnonymous: Boolean by SharedPreference(SP_BLUETOOTH_IGNORE_ANONYMOUS, true)
|
||||
}
|
||||
|
||||
init {
|
||||
|
@ -1,10 +1,13 @@
|
||||
package com.idormy.sms.forwarder.utils.task
|
||||
|
||||
import android.bluetooth.BluetoothAdapter
|
||||
import android.bluetooth.BluetoothDevice
|
||||
import android.os.BatteryManager
|
||||
import com.google.gson.Gson
|
||||
import com.idormy.sms.forwarder.database.entity.Rule
|
||||
import com.idormy.sms.forwarder.entity.TaskSetting
|
||||
import com.idormy.sms.forwarder.entity.condition.BatterySetting
|
||||
import com.idormy.sms.forwarder.entity.condition.BluetoothSetting
|
||||
import com.idormy.sms.forwarder.entity.condition.ChargeSetting
|
||||
import com.idormy.sms.forwarder.entity.condition.CronSetting
|
||||
import com.idormy.sms.forwarder.entity.condition.LocationSetting
|
||||
@ -15,6 +18,7 @@ import com.idormy.sms.forwarder.utils.DELAY_TIME_AFTER_SIM_READY
|
||||
import com.idormy.sms.forwarder.utils.Log
|
||||
import com.idormy.sms.forwarder.utils.TASK_CONDITION_APP
|
||||
import com.idormy.sms.forwarder.utils.TASK_CONDITION_BATTERY
|
||||
import com.idormy.sms.forwarder.utils.TASK_CONDITION_BLUETOOTH
|
||||
import com.idormy.sms.forwarder.utils.TASK_CONDITION_CALL
|
||||
import com.idormy.sms.forwarder.utils.TASK_CONDITION_CHARGE
|
||||
import com.idormy.sms.forwarder.utils.TASK_CONDITION_CRON
|
||||
@ -228,6 +232,49 @@ class ConditionUtils private constructor() {
|
||||
//TODO: 判断消息是否满足条件
|
||||
}
|
||||
|
||||
TASK_CONDITION_BLUETOOTH -> {
|
||||
val bluetoothSetting = Gson().fromJson(condition.setting, BluetoothSetting::class.java)
|
||||
if (bluetoothSetting == null) {
|
||||
Log.d(TAG, "TASK-$taskId:bluetoothSetting is null")
|
||||
continue
|
||||
}
|
||||
|
||||
when (bluetoothSetting.action) {
|
||||
BluetoothAdapter.ACTION_STATE_CHANGED -> {
|
||||
if (TaskUtils.bluetoothState != bluetoothSetting.state) {
|
||||
Log.d(TAG, "TASK-$taskId:bluetoothState is not match, bluetoothSetting = $bluetoothSetting")
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
BluetoothDevice.ACTION_ACL_CONNECTED -> {
|
||||
if (!TaskUtils.connectedDevices.containsKey(bluetoothSetting.device)) {
|
||||
Log.d(TAG, "TASK-$taskId:device is not connected, bluetoothSetting = $bluetoothSetting")
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
BluetoothDevice.ACTION_ACL_DISCONNECTED -> {
|
||||
if (TaskUtils.connectedDevices.containsKey(bluetoothSetting.device)) {
|
||||
Log.d(TAG, "TASK-$taskId:device is connected, bluetoothSetting = $bluetoothSetting")
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
BluetoothAdapter.ACTION_DISCOVERY_FINISHED -> {
|
||||
if (bluetoothSetting.result == 1 && !TaskUtils.discoveredDevices.containsKey(bluetoothSetting.device)) {
|
||||
Log.d(TAG, "TASK-$taskId:device is not discovered, bluetoothSetting = $bluetoothSetting")
|
||||
return false
|
||||
} else if (bluetoothSetting.result == 0 && TaskUtils.discoveredDevices.containsKey(bluetoothSetting.device)) {
|
||||
Log.d(TAG, "TASK-$taskId:device is discovered, bluetoothSetting = $bluetoothSetting")
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Log.d(TAG, "TASK-$taskId:bluetoothAction is match, bluetoothSetting = $bluetoothSetting")
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
package com.idormy.sms.forwarder.utils.task
|
||||
|
||||
import android.bluetooth.BluetoothAdapter
|
||||
import android.os.BatteryManager
|
||||
import com.idormy.sms.forwarder.R
|
||||
import com.idormy.sms.forwarder.entity.LocationInfo
|
||||
@ -8,7 +9,10 @@ import com.idormy.sms.forwarder.utils.SP_BATTERY_LEVEL
|
||||
import com.idormy.sms.forwarder.utils.SP_BATTERY_PCT
|
||||
import com.idormy.sms.forwarder.utils.SP_BATTERY_PLUGGED
|
||||
import com.idormy.sms.forwarder.utils.SP_BATTERY_STATUS
|
||||
import com.idormy.sms.forwarder.utils.SP_BLUETOOTH_STATE
|
||||
import com.idormy.sms.forwarder.utils.SP_CONNECTED_DEVICE
|
||||
import com.idormy.sms.forwarder.utils.SP_DATA_SIM_SLOT
|
||||
import com.idormy.sms.forwarder.utils.SP_DISCOVERED_DEVICES
|
||||
import com.idormy.sms.forwarder.utils.SP_IPV4
|
||||
import com.idormy.sms.forwarder.utils.SP_IPV6
|
||||
import com.idormy.sms.forwarder.utils.SP_LOCATION_INFO_NEW
|
||||
@ -30,6 +34,7 @@ import com.idormy.sms.forwarder.utils.TASK_ACTION_SENDSMS
|
||||
import com.idormy.sms.forwarder.utils.TASK_ACTION_SETTINGS
|
||||
import com.idormy.sms.forwarder.utils.TASK_CONDITION_APP
|
||||
import com.idormy.sms.forwarder.utils.TASK_CONDITION_BATTERY
|
||||
import com.idormy.sms.forwarder.utils.TASK_CONDITION_BLUETOOTH
|
||||
import com.idormy.sms.forwarder.utils.TASK_CONDITION_CALL
|
||||
import com.idormy.sms.forwarder.utils.TASK_CONDITION_CHARGE
|
||||
import com.idormy.sms.forwarder.utils.TASK_CONDITION_CRON
|
||||
@ -61,6 +66,7 @@ class TaskUtils private constructor() {
|
||||
TASK_CONDITION_SMS -> R.drawable.auto_task_icon_sms
|
||||
TASK_CONDITION_CALL -> R.drawable.auto_task_icon_incall
|
||||
TASK_CONDITION_APP -> R.drawable.auto_task_icon_start_activity
|
||||
TASK_CONDITION_BLUETOOTH -> R.drawable.auto_task_icon_bluetooth
|
||||
TASK_ACTION_SENDSMS -> R.drawable.auto_task_icon_sms
|
||||
TASK_ACTION_NOTIFICATION -> R.drawable.auto_task_icon_notification
|
||||
TASK_ACTION_CLEANER -> R.drawable.auto_task_icon_cleaner
|
||||
@ -89,6 +95,7 @@ class TaskUtils private constructor() {
|
||||
TASK_CONDITION_SMS -> R.drawable.auto_task_icon_sms_grey
|
||||
TASK_CONDITION_CALL -> R.drawable.auto_task_icon_incall_grey
|
||||
TASK_CONDITION_APP -> R.drawable.auto_task_icon_start_activity_grey
|
||||
TASK_CONDITION_BLUETOOTH -> R.drawable.auto_task_icon_bluetooth_grey
|
||||
TASK_ACTION_SENDSMS -> R.drawable.auto_task_icon_sms_grey
|
||||
TASK_ACTION_NOTIFICATION -> R.drawable.auto_task_icon_notification_grey
|
||||
TASK_ACTION_CLEANER -> R.drawable.auto_task_icon_cleaner_grey
|
||||
@ -145,5 +152,14 @@ class TaskUtils private constructor() {
|
||||
//上次锁屏广播
|
||||
var lockScreenAction: String by SharedPreference(SP_LOCK_SCREEN_ACTION, "")
|
||||
|
||||
//已发现的蓝牙设备
|
||||
var discoveredDevices: MutableMap<String, String> by SharedPreference(SP_DISCOVERED_DEVICES, mutableMapOf())
|
||||
|
||||
//已连接的蓝牙设备
|
||||
var connectedDevices: MutableMap<String, String> by SharedPreference(SP_CONNECTED_DEVICE, mutableMapOf())
|
||||
|
||||
//蓝牙状态
|
||||
var bluetoothState: Int by SharedPreference(SP_BLUETOOTH_STATE, BluetoothAdapter.STATE_ON)
|
||||
|
||||
}
|
||||
}
|
@ -28,6 +28,7 @@ import com.idormy.sms.forwarder.entity.action.SmsSetting
|
||||
import com.idormy.sms.forwarder.entity.action.TaskActionSetting
|
||||
import com.idormy.sms.forwarder.service.HttpServerService
|
||||
import com.idormy.sms.forwarder.service.LocationService
|
||||
import com.idormy.sms.forwarder.utils.ACTION_RESTART
|
||||
import com.idormy.sms.forwarder.utils.CacheUtils
|
||||
import com.idormy.sms.forwarder.utils.EVENT_ALARM_ACTION
|
||||
import com.idormy.sms.forwarder.utils.EVENT_TOAST_ERROR
|
||||
@ -203,7 +204,7 @@ class ActionWorker(context: Context, params: WorkerParameters) : CoroutineWorker
|
||||
|
||||
if (settingsSetting.enableLocation) {
|
||||
val serviceIntent = Intent(App.context, LocationService::class.java)
|
||||
serviceIntent.action = "RESTART"
|
||||
serviceIntent.action = ACTION_RESTART
|
||||
App.context.startService(serviceIntent)
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,115 @@
|
||||
package com.idormy.sms.forwarder.workers
|
||||
|
||||
import android.bluetooth.BluetoothAdapter
|
||||
import android.bluetooth.BluetoothDevice
|
||||
import android.content.Context
|
||||
import androidx.work.CoroutineWorker
|
||||
import androidx.work.Data
|
||||
import androidx.work.OneTimeWorkRequestBuilder
|
||||
import androidx.work.WorkManager
|
||||
import androidx.work.WorkerParameters
|
||||
import com.google.gson.Gson
|
||||
import com.idormy.sms.forwarder.core.Core
|
||||
import com.idormy.sms.forwarder.entity.MsgInfo
|
||||
import com.idormy.sms.forwarder.entity.TaskSetting
|
||||
import com.idormy.sms.forwarder.entity.condition.BluetoothSetting
|
||||
import com.idormy.sms.forwarder.utils.Log
|
||||
import com.idormy.sms.forwarder.utils.TaskWorker
|
||||
import com.idormy.sms.forwarder.utils.task.ConditionUtils
|
||||
import java.util.Date
|
||||
|
||||
@Suppress("PrivatePropertyName", "DEPRECATION")
|
||||
class BluetoothWorker(context: Context, params: WorkerParameters) : CoroutineWorker(context, params) {
|
||||
|
||||
private val TAG: String = BluetoothWorker::class.java.simpleName
|
||||
|
||||
override suspend fun doWork(): Result {
|
||||
try {
|
||||
val conditionType = inputData.getInt(TaskWorker.CONDITION_TYPE, -1)
|
||||
val action = inputData.getString(TaskWorker.ACTION) ?: BluetoothAdapter.ACTION_STATE_CHANGED
|
||||
val msg = inputData.getString(TaskWorker.MSG) ?: "1"
|
||||
val taskList = Core.task.getByType(conditionType)
|
||||
for (task in taskList) {
|
||||
Log.d(TAG, "task = $task")
|
||||
|
||||
// 根据任务信息执行相应操作
|
||||
val conditionList = Gson().fromJson(task.conditions, Array<TaskSetting>::class.java).toMutableList()
|
||||
if (conditionList.isEmpty()) {
|
||||
Log.d(TAG, "TASK-${task.id}:conditionList is empty")
|
||||
continue
|
||||
}
|
||||
val firstCondition = conditionList.firstOrNull()
|
||||
if (firstCondition == null) {
|
||||
Log.d(TAG, "TASK-${task.id}:firstCondition is null")
|
||||
continue
|
||||
}
|
||||
|
||||
val bluetoothSetting = Gson().fromJson(firstCondition.setting, BluetoothSetting::class.java)
|
||||
if (bluetoothSetting == null) {
|
||||
Log.d(TAG, "TASK-${task.id}:bluetoothSetting is null")
|
||||
continue
|
||||
}
|
||||
|
||||
if (action != bluetoothSetting.action) {
|
||||
Log.d(TAG, "TASK-${task.id}:action is not match, bluetoothSetting = $bluetoothSetting")
|
||||
continue
|
||||
}
|
||||
|
||||
var content = ""
|
||||
when (action) {
|
||||
BluetoothAdapter.ACTION_STATE_CHANGED -> {
|
||||
if (msg != bluetoothSetting.state.toString()) {
|
||||
Log.d(TAG, "TASK-${task.id}:bluetoothState is not match, bluetoothSetting = $bluetoothSetting")
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
BluetoothDevice.ACTION_ACL_CONNECTED, BluetoothDevice.ACTION_ACL_DISCONNECTED -> {
|
||||
val devices = Gson().fromJson(msg, MutableMap::class.java)
|
||||
Log.d(TAG, "TASK-${task.id}:devices = $devices")
|
||||
if (devices.isEmpty() || !devices.containsKey(bluetoothSetting.device)) {
|
||||
Log.d(TAG, "TASK-${task.id}:device is not match, bluetoothSetting = $bluetoothSetting")
|
||||
continue
|
||||
}
|
||||
for ((k, v) in devices) {
|
||||
content += "$k ($v)\n"
|
||||
}
|
||||
}
|
||||
|
||||
BluetoothAdapter.ACTION_DISCOVERY_FINISHED -> {
|
||||
val devices = Gson().fromJson(msg, MutableMap::class.java)
|
||||
Log.d(TAG, "TASK-${task.id}:devices = $devices")
|
||||
if (bluetoothSetting.result == 1 && !devices.containsKey(bluetoothSetting.device)) {
|
||||
Log.d(TAG, "TASK-${task.id}:device is not discovered, bluetoothSetting = $bluetoothSetting")
|
||||
continue
|
||||
} else if (bluetoothSetting.result == 0 && devices.containsKey(bluetoothSetting.device)) {
|
||||
Log.d(TAG, "TASK-${task.id}:device is discovered, bluetoothSetting = $bluetoothSetting")
|
||||
continue
|
||||
}
|
||||
for ((k, v) in devices) {
|
||||
content += "$k ($v)\n"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//TODO:判断其他条件是否满足
|
||||
if (!ConditionUtils.checkCondition(task.id, conditionList)) {
|
||||
Log.d(TAG, "TASK-${task.id}:other condition is not satisfied")
|
||||
continue
|
||||
}
|
||||
|
||||
//TODO: 组装消息体 && 执行具体任务
|
||||
val msgInfo = MsgInfo("task", task.name, content.trim(), Date(), task.description)
|
||||
val actionData = Data.Builder().putLong(TaskWorker.TASK_ID, task.id).putString(TaskWorker.TASK_ACTIONS, task.actions).putString(TaskWorker.MSG_INFO, Gson().toJson(msgInfo)).build()
|
||||
val actionRequest = OneTimeWorkRequestBuilder<ActionWorker>().setInputData(actionData).build()
|
||||
WorkManager.getInstance().enqueue(actionRequest)
|
||||
}
|
||||
|
||||
return Result.success()
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Error running worker: ${e.message}", e)
|
||||
return Result.failure()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
15
app/src/main/res/drawable/auto_task_icon_bluetooth.xml
Normal file
15
app/src/main/res/drawable/auto_task_icon_bluetooth.xml
Normal file
@ -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="#ff2eafff"
|
||||
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>
|
15
app/src/main/res/drawable/auto_task_icon_bluetooth_grey.xml
Normal file
15
app/src/main/res/drawable/auto_task_icon_bluetooth_grey.xml
Normal file
@ -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="#ffe6e6e6"
|
||||
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>
|
17
app/src/main/res/drawable/ic_bt_bluetooth.xml
Normal file
17
app/src/main/res/drawable/ic_bt_bluetooth.xml
Normal file
@ -0,0 +1,17 @@
|
||||
<?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:viewportWidth="76.0"
|
||||
android:viewportHeight="76.0">
|
||||
<path
|
||||
android:fillColor="#ff99a1bd"
|
||||
android:pathData="M0,38C0,17.0132 17.0132,0 38,0V0C58.9868,0 76,17.0132 76,38V38C76,58.9868 58.9868,76 38,76V76C17.0132,76 0,58.9868 0,38V38Z" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M23,32.3444L35.8889,37.855V22L52,30.6042L36.1574,38.0483L52,45.4924L35.8889,54V38.145L23,43.6556"
|
||||
android:strokeWidth="4.0"
|
||||
android:strokeColor="#ffffffff"
|
||||
android:strokeLineCap="round"
|
||||
android:strokeLineJoin="round" />
|
||||
</vector>
|
15
app/src/main/res/drawable/ic_bt_cellphone.xml
Normal file
15
app/src/main/res/drawable/ic_bt_cellphone.xml
Normal file
@ -0,0 +1,15 @@
|
||||
<?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:viewportWidth="76.0"
|
||||
android:viewportHeight="76.0">
|
||||
<path
|
||||
android:fillColor="#ff13d4b2"
|
||||
android:pathData="M0,38C0,17.0132 17.0132,0 38,0V0C58.9868,0 76,17.0132 76,38V38C76,58.9868 58.9868,76 38,76V76C17.0132,76 0,58.9868 0,38V38Z" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M31,21L45,21A6,6 0,0 1,51 27L51,49A6,6 0,0 1,45 55L31,55A6,6 0,0 1,25 49L25,27A6,6 0,0 1,31 21z"
|
||||
android:strokeWidth="4.0"
|
||||
android:strokeColor="#ffffffff" />
|
||||
</vector>
|
14
app/src/main/res/drawable/ic_bt_headphones.xml
Normal file
14
app/src/main/res/drawable/ic_bt_headphones.xml
Normal file
@ -0,0 +1,14 @@
|
||||
<?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:viewportWidth="76.0"
|
||||
android:viewportHeight="76.0">
|
||||
<path
|
||||
android:fillColor="#ff2eafff"
|
||||
android:pathData="M0,38C0,17.0132 17.0132,0 38,0V0C58.9868,0 76,17.0132 76,38V38C76,58.9868 58.9868,76 38,76V76C17.0132,76 0,58.9868 0,38V38Z" />
|
||||
<path
|
||||
android:fillColor="#ffffffff"
|
||||
android:fillType="evenOdd"
|
||||
android:pathData="M38,19C30.268,19 24,25.268 24,33V49C24,50.1046 24.8954,51 26,51C27.1046,51 28,50.1046 28,49V33C28,27.4772 32.4772,23 38,23C43.5228,23 48,27.4772 48,33V49C48,50.1046 48.8954,51 50,51C51.1046,51 52,50.1046 52,49V33C52,25.268 45.732,19 38,19ZM20,39C21.1046,39 22,39.8954 22,41L22,49C22,50.1046 21.1046,51 20,51C18.8954,51 18,50.1046 18,49V41C18,39.8954 18.8954,39 20,39ZM56,39C57.1046,39 58,39.8954 58,41V49C58,50.1046 57.1046,51 56,51C54.8954,51 54,50.1046 54,49V41C54,39.8954 54.8954,39 56,39Z" />
|
||||
</vector>
|
14
app/src/main/res/drawable/ic_bt_headset_hfp.xml
Normal file
14
app/src/main/res/drawable/ic_bt_headset_hfp.xml
Normal file
@ -0,0 +1,14 @@
|
||||
<?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:viewportWidth="76.0"
|
||||
android:viewportHeight="76.0">
|
||||
<path
|
||||
android:fillColor="#ff2eafff"
|
||||
android:pathData="M0,38C0,17.0132 17.0132,0 38,0V0C58.9868,0 76,17.0132 76,38V38C76,58.9868 58.9868,76 38,76V76C17.0132,76 0,58.9868 0,38V38Z" />
|
||||
<path
|
||||
android:fillColor="#ffffffff"
|
||||
android:fillType="evenOdd"
|
||||
android:pathData="M38,19C30.268,19 24,25.268 24,33V49C24,50.1046 24.8954,51 26,51C27.1046,51 28,50.1046 28,49V33C28,27.4772 32.4772,23 38,23C43.5228,23 48,27.4772 48,33V45C48,46.1046 47.1046,47 46,47H40C38.8954,47 38,47.8954 38,49C38,50.1046 38.8954,51 40,51H46C49.3137,51 52,48.3137 52,45V33C52,25.268 45.732,19 38,19ZM20,39C21.1046,39 22,39.8954 22,41L22,49C22,50.1046 21.1046,51 20,51C18.8954,51 18,50.1046 18,49V41C18,39.8954 18.8954,39 20,39ZM56,39C57.1046,39 58,39.8954 58,41V49C58,50.1046 57.1046,51 56,51C54.8954,51 54,50.1046 54,49V41C54,39.8954 54.8954,39 56,39Z" />
|
||||
</vector>
|
25
app/src/main/res/drawable/ic_bt_imaging.xml
Normal file
25
app/src/main/res/drawable/ic_bt_imaging.xml
Normal file
@ -0,0 +1,25 @@
|
||||
<?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:viewportWidth="76.0"
|
||||
android:viewportHeight="76.0">
|
||||
<path
|
||||
android:fillColor="#fffa9a2a"
|
||||
android:pathData="M0,38C0,17.0132 17.0132,0 38,0V0C58.9868,0 76,17.0132 76,38V38C76,58.9868 58.9868,76 38,76V76C17.0132,76 0,58.9868 0,38V38Z" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M20,46L20,32A4,4 0,0 1,24 28L52,28A4,4 0,0 1,56 32L56,46A4,4 0,0 1,52 50L24,50A4,4 0,0 1,20 46z"
|
||||
android:strokeWidth="4.0"
|
||||
android:strokeColor="#ffffffff" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M29,28L29,24.8C29,23.1198 29,22.2798 29.327,21.638C29.6146,21.0735 30.0735,20.6146 30.638,20.327C31.2798,20 32.1198,20 33.8,20L42.2,20C43.8802,20 44.7202,20 45.362,20.327C45.9265,20.6146 46.3854,21.0735 46.673,21.638C47,22.2798 47,23.1198 47,24.8L47,28L29,28Z"
|
||||
android:strokeWidth="4.0"
|
||||
android:strokeColor="#ffffffff" />
|
||||
<path
|
||||
android:fillColor="#fffa9a2a"
|
||||
android:pathData="M33.8,57C32.1198,57 31.2798,57 30.638,56.673C30.0735,56.3854 29.6146,55.9265 29.327,55.362C29,54.7202 29,53.8802 29,52.2L29,40L47,40L47,52.2C47,53.8802 47,54.7202 46.673,55.362C46.3854,55.9265 45.9265,56.3854 45.362,56.673C44.7202,57 43.8802,57 42.2,57L33.8,57Z"
|
||||
android:strokeWidth="4.0"
|
||||
android:strokeColor="#ffffffff" />
|
||||
</vector>
|
14
app/src/main/res/drawable/ic_bt_laptop.xml
Normal file
14
app/src/main/res/drawable/ic_bt_laptop.xml
Normal file
@ -0,0 +1,14 @@
|
||||
<?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:viewportWidth="76.0"
|
||||
android:viewportHeight="76.0">
|
||||
<path
|
||||
android:fillColor="#ff13d4b2"
|
||||
android:pathData="M0,38C0,17.0132 17.0132,0 38,0V0C58.9868,0 76,17.0132 76,38V38C76,58.9868 58.9868,76 38,76V76C17.0132,76 0,58.9868 0,38V38Z" />
|
||||
<path
|
||||
android:fillColor="#ffffffff"
|
||||
android:fillType="evenOdd"
|
||||
android:pathData="M29.48,23L29.4003,23L29.4003,23C28.3105,23 27.3704,22.9999 26.5969,23.0652C25.78,23.1341 24.9686,23.2865 24.1924,23.6974C23.1295,24.2602 22.2602,25.1295 21.6974,26.1924C21.2864,26.9686 21.1341,27.78 21.0652,28.5969C20.9999,29.3704 21,30.3105 21,31.4002V31.4003L21,31.48V44.4286L18.5535,46.7586L18.4972,46.8122L18.4971,46.8123C18.0787,47.2106 17.6711,47.5988 17.373,47.9391C17.1115,48.2377 16.5727,48.8811 16.5077,49.7848C16.4417,50.7028 16.8007,51.6003 17.4816,52.2195C18.152,52.8291 18.9858,52.9235 19.3811,52.9593C19.8316,53.0002 20.3945,53.0001 20.9723,53L21.05,53H54.95L55.0278,53C55.6055,53.0001 56.1684,53.0002 56.619,52.9593C57.0142,52.9235 57.8481,52.8291 58.5184,52.2195C59.1993,51.6003 59.5583,50.7028 59.4923,49.7848C59.4273,48.8811 58.8885,48.2377 58.627,47.9391C58.329,47.5988 57.9213,47.2106 57.5029,46.8123L57.4466,46.7586L55,44.4286V31.48V31.4003V31.4002C55,30.3104 55.0001,29.3704 54.9348,28.5969C54.8659,27.78 54.7136,26.9686 54.3026,26.1924C53.7398,25.1295 52.8705,24.2602 51.8076,23.6974C51.0314,23.2865 50.22,23.1341 49.4031,23.0652C48.6296,22.9999 47.6895,23 46.5997,23L46.52,23H29.48ZM26.0641,27.2325C26.1661,27.1785 26.3727,27.0983 26.9332,27.051C27.5183,27.0017 28.288,27 29.48,27H46.52C47.712,27 48.4817,27.0017 49.0668,27.051C49.6273,27.0983 49.8339,27.1785 49.9359,27.2325C50.2902,27.4201 50.5799,27.7098 50.7675,28.0641C50.8215,28.1661 50.9017,28.3727 50.949,28.9332C50.9983,29.5183 51,30.288 51,31.48V43H25V31.48C25,30.288 25.0017,29.5183 25.051,28.9332C25.0983,28.3727 25.1785,28.1661 25.2325,28.0641C25.4201,27.7098 25.7098,27.4201 26.0641,27.2325ZM24.1,47L22,49H54L51.9,47H24.1Z" />
|
||||
</vector>
|
19
app/src/main/res/drawable/ic_bt_misc_hid.xml
Normal file
19
app/src/main/res/drawable/ic_bt_misc_hid.xml
Normal file
@ -0,0 +1,19 @@
|
||||
<?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:viewportWidth="76.0"
|
||||
android:viewportHeight="76.0">
|
||||
<path
|
||||
android:fillColor="#ff565bdf"
|
||||
android:pathData="M0,38C0,17.0132 17.0132,0 38,0V0C58.9868,0 76,17.0132 76,38V38C76,58.9868 58.9868,76 38,76V76C17.0132,76 0,58.9868 0,38V38Z" />
|
||||
<path
|
||||
android:fillColor="#ffffffff"
|
||||
android:fillType="evenOdd"
|
||||
android:pathData="M46.5,32.5C46.5,33.6046 45.6046,34.5 44.5,34.5C43.3954,34.5 42.5,33.6046 42.5,32.5C42.5,31.3954 43.3954,30.5 44.5,30.5C45.6046,30.5 46.5,31.3954 46.5,32.5ZM42.5,36.5C42.5,37.6046 41.6046,38.5 40.5,38.5C39.3954,38.5 38.5,37.6046 38.5,36.5C38.5,35.3954 39.3954,34.5 40.5,34.5C41.6046,34.5 42.5,35.3954 42.5,36.5ZM48.5,38.5C49.6046,38.5 50.5,37.6046 50.5,36.5C50.5,35.3954 49.6046,34.5 48.5,34.5C47.3954,34.5 46.5,35.3954 46.5,36.5C46.5,37.6046 47.3954,38.5 48.5,38.5ZM46.5,40.5C46.5,41.6046 45.6046,42.5 44.5,42.5C43.3954,42.5 42.5,41.6046 42.5,40.5C42.5,39.3954 43.3954,38.5 44.5,38.5C45.6046,38.5 46.5,39.3954 46.5,40.5ZM30,31C30.8284,31 31.5,31.6716 31.5,32.5V35H34C34.8284,35 35.5,35.6716 35.5,36.5C35.5,37.3284 34.8284,38 34,38H31.5V40.5C31.5,41.3284 30.8284,42 30,42C29.1716,42 28.5,41.3284 28.5,40.5V38H26C25.1716,38 24.5,37.3284 24.5,36.5C24.5,35.6716 25.1716,35 26,35H28.5V32.5C28.5,31.6716 29.1716,31 30,31Z" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M20,32.6C20,29.2397 20,27.5595 20.654,26.2761C21.2292,25.1471 22.1471,24.2292 23.2761,23.654C24.5595,23 26.2397,23 29.6,23H45.4C48.7603,23 50.4405,23 51.7239,23.654C52.8529,24.2292 53.7708,25.1471 54.346,26.2761C55,27.5595 55,29.2397 55,32.6V46.0043C55,49.3157 52.3157,52 49.0043,52V52C47.8588,52 46.7407,51.6685 45.7453,51.1016C43.6741,49.9223 39.9263,48 37.5,48C35.0737,48 31.3259,49.9223 29.2547,51.1016C28.2593,51.6685 27.1412,52 25.9957,52V52C22.6843,52 20,49.3157 20,46.0043V32.6Z"
|
||||
android:strokeWidth="4.0"
|
||||
android:strokeColor="#ffffffff" />
|
||||
</vector>
|
23
app/src/main/res/drawable/ic_bt_network_pan.xml
Normal file
23
app/src/main/res/drawable/ic_bt_network_pan.xml
Normal file
@ -0,0 +1,23 @@
|
||||
<?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:viewportWidth="76.0"
|
||||
android:viewportHeight="76.0">
|
||||
<path
|
||||
android:fillColor="#ff99a1bd"
|
||||
android:pathData="M0,38C0,17.0132 17.0132,0 38,0V0C58.9868,0 76,17.0132 76,38V38C76,58.9868 58.9868,76 38,76V76C17.0132,76 0,58.9868 0,38V38Z" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M23,32.3444L35.8889,37.855V22L52,30.6042L36.1574,38.0483L52,45.4924L35.8889,54V38.145L23,43.6556"
|
||||
android:strokeWidth="4.0"
|
||||
android:strokeColor="#ffffffff"
|
||||
android:strokeLineCap="round"
|
||||
android:strokeLineJoin="round" />
|
||||
<path
|
||||
android:fillColor="#ffffffff"
|
||||
android:pathData="M59.5,38.5m-2.5,0a2.5,2.5 0,1 1,5 0a2.5,2.5 0,1 1,-5 0" />
|
||||
<path
|
||||
android:fillColor="#ffffffff"
|
||||
android:pathData="M14.5,38.5m-2.5,0a2.5,2.5 0,1 1,5 0a2.5,2.5 0,1 1,-5 0" />
|
||||
</vector>
|
19
app/src/main/res/drawable/ic_bt_wristband.xml
Normal file
19
app/src/main/res/drawable/ic_bt_wristband.xml
Normal file
@ -0,0 +1,19 @@
|
||||
<?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:viewportWidth="76.0"
|
||||
android:viewportHeight="76.0">
|
||||
<path
|
||||
android:fillColor="#fff36537"
|
||||
android:pathData="M38,0L38,0A38,38 0,0 1,76 38L76,38A38,38 0,0 1,38 76L38,76A38,38 0,0 1,0 38L0,38A38,38 0,0 1,38 0z" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M51.2306,29C52.9783,31.5642 54,34.6628 54,38C54,46.8366 46.8366,54 38,54C29.1634,54 22,46.8366 22,38C22,34.6628 23.0217,31.5642 24.7694,29"
|
||||
android:strokeWidth="4.0"
|
||||
android:strokeColor="#ffffffff"
|
||||
android:strokeLineCap="round" />
|
||||
<path
|
||||
android:fillColor="#ffffffff"
|
||||
android:pathData="M28,22.6667C28,21.7462 28.7462,21 29.6667,21H46.3333C47.2538,21 48,21.7462 48,22.6667C48,24.5076 46.5076,26 44.6667,26H31.3333C29.4924,26 28,24.5076 28,22.6667Z" />
|
||||
</vector>
|
72
app/src/main/res/layout/adapter_bluetooth_list_item.xml
Normal file
72
app/src/main/res/layout/adapter_bluetooth_list_item.xml
Normal file
@ -0,0 +1,72 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@color/xui_config_color_white"
|
||||
android:orientation="vertical"
|
||||
tools:ignore="UseCompoundDrawables">
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:background="?attr/xui_config_color_separator_light" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal"
|
||||
android:paddingTop="@dimen/config_padding_5dp"
|
||||
android:paddingBottom="@dimen/config_padding_5dp">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/image_device_icon"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:src="@drawable/auto_task_icon_bluetooth"
|
||||
tools:ignore="ContentDescription" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_weight="1"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/text_device_name"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/text_device_address"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textColor="@android:color/darker_gray"
|
||||
android:textSize="12sp" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_edit"
|
||||
android:layout_width="20dp"
|
||||
android:layout_height="20dp"
|
||||
android:layout_marginStart="5dp"
|
||||
android:src="@drawable/ic_edit"
|
||||
app:tint="@color/toast_info_color"
|
||||
tools:ignore="ContentDescription,PrivateResource,ImageContrastCheck" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_remove"
|
||||
android:layout_width="20dp"
|
||||
android:layout_height="20dp"
|
||||
android:src="@drawable/ic_delete"
|
||||
app:tint="@color/toast_error_color"
|
||||
tools:ignore="ContentDescription,PrivateResource" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
@ -343,6 +343,118 @@
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
style="@style/BarStyle.Switch"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="vertical">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/enable_bluetooth"
|
||||
android:textStyle="bold"
|
||||
tools:ignore="RelativeOverlap" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/enable_bluetooth_tips"
|
||||
android:textSize="@dimen/text_size_mini"
|
||||
tools:ignore="SmallSp" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<com.xuexiang.xui.widget.button.switchbutton.SwitchButton
|
||||
android:id="@+id/sb_enable_bluetooth"
|
||||
style="@style/SwitchButtonStyle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
tools:ignore="TouchTargetSizeCheck,DuplicateSpeakableTextCheck" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/layout_bluetooth_setting"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="5dp"
|
||||
android:orientation="vertical">
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:background="?attr/xui_config_color_separator_light" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="5dp"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="5dp"
|
||||
android:text="@string/scan_interval"
|
||||
android:textSize="@dimen/text_size_small"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<com.xuexiang.xui.widget.picker.XSeekBar
|
||||
android:id="@+id/xsb_scan_interval"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="5dp"
|
||||
android:layout_weight="1"
|
||||
android:padding="0dp"
|
||||
app:xsb_max="120"
|
||||
app:xsb_min="1" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="5dp"
|
||||
android:text="@string/seconds"
|
||||
android:textSize="@dimen/text_size_small"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<com.xuexiang.xui.widget.button.SmoothCheckBox
|
||||
android:id="@+id/scb_ignore_anonymous"
|
||||
android:layout_width="15dp"
|
||||
android:layout_height="15dp"
|
||||
android:layout_marginStart="@dimen/config_margin_10dp"
|
||||
app:scb_color_checked="@color/colorPrimary"
|
||||
tools:ignore="SpeakableTextPresentCheck,TouchTargetSizeCheck" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="2dp"
|
||||
android:singleLine="true"
|
||||
android:text="@string/ignore_anonymous"
|
||||
android:textSize="@dimen/text_size_small"
|
||||
tools:ignore="SmallSp" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
style="@style/BarStyle.Switch"
|
||||
android:layout_width="match_parent"
|
||||
|
258
app/src/main/res/layout/fragment_tasks_condition_bluetooth.xml
Normal file
258
app/src/main/res/layout/fragment_tasks_condition_bluetooth.xml
Normal file
@ -0,0 +1,258 @@
|
||||
<?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:gravity="center_horizontal"
|
||||
android:orientation="vertical">
|
||||
|
||||
<androidx.appcompat.widget.AppCompatImageView
|
||||
android:layout_width="120dp"
|
||||
android:layout_height="120dp"
|
||||
android:layout_margin="10dp"
|
||||
android:contentDescription="@string/task_bluetooth"
|
||||
app:srcCompat="@drawable/auto_task_icon_bluetooth"
|
||||
tools:ignore="ImageContrastCheck" />
|
||||
|
||||
<LinearLayout
|
||||
style="@style/BarStyle"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/task_bluetooth"
|
||||
android:textSize="@dimen/text_size_big"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_description"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/task_bluetooth_tips"
|
||||
android:textSize="@dimen/text_size_mini"
|
||||
tools:ignore="SmallSp" />
|
||||
|
||||
<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" />
|
||||
|
||||
<RadioGroup
|
||||
android:id="@+id/rg_bluetooth_action"
|
||||
style="@style/rg_style"
|
||||
android:orientation="vertical"
|
||||
android:paddingBottom="@dimen/config_padding_5dp">
|
||||
|
||||
<RadioButton
|
||||
android:id="@+id/rb_action_state_changed"
|
||||
style="@style/rg_rb_style_match"
|
||||
android:checked="true"
|
||||
android:text="@string/bluetooth_state_changed"
|
||||
tools:ignore="TouchTargetSizeCheck" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/layout_bluetooth_state"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="15dp"
|
||||
android:layout_marginEnd="@dimen/config_margin_10dp"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:text="@string/specified_state"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<RadioGroup
|
||||
android:id="@+id/rg_bluetooth_state"
|
||||
style="@style/rg_style"
|
||||
android:layout_marginStart="15dp"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<RadioButton
|
||||
android:id="@+id/rb_state_on"
|
||||
style="@style/rg_rb_style"
|
||||
android:checked="true"
|
||||
android:text="@string/state_on"
|
||||
tools:ignore="TouchTargetSizeCheck" />
|
||||
|
||||
<RadioButton
|
||||
android:id="@+id/rb_state_off"
|
||||
style="@style/rg_rb_style"
|
||||
android:text="@string/state_off"
|
||||
tools:ignore="TouchTargetSizeCheck" />
|
||||
|
||||
</RadioGroup>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<RadioButton
|
||||
android:id="@+id/rb_action_discovery_finished"
|
||||
style="@style/rg_rb_style_match"
|
||||
android:text="@string/bluetooth_discovery_finished"
|
||||
tools:ignore="TouchTargetSizeCheck" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/layout_discovery_finished"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="15dp"
|
||||
android:layout_marginEnd="@dimen/config_margin_10dp"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:text="@string/specified_result"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<RadioGroup
|
||||
android:id="@+id/rg_discovery_result"
|
||||
style="@style/rg_style"
|
||||
android:layout_marginStart="15dp"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<RadioButton
|
||||
android:id="@+id/rb_discovered"
|
||||
style="@style/rg_rb_style"
|
||||
android:checked="true"
|
||||
android:text="@string/discovered"
|
||||
tools:ignore="TouchTargetSizeCheck" />
|
||||
|
||||
<RadioButton
|
||||
android:id="@+id/rb_undiscovered"
|
||||
style="@style/rg_rb_style"
|
||||
android:text="@string/undiscovered"
|
||||
tools:ignore="TouchTargetSizeCheck" />
|
||||
|
||||
</RadioGroup>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<RadioButton
|
||||
android:id="@+id/rb_action_acl_connected"
|
||||
style="@style/rg_rb_style_match"
|
||||
android:text="@string/bluetooth_acl_connected"
|
||||
tools:ignore="TouchTargetSizeCheck" />
|
||||
|
||||
<RadioButton
|
||||
android:id="@+id/rb_action_acl_disconnected"
|
||||
style="@style/rg_rb_style_match"
|
||||
android:text="@string/bluetooth_acl_disconnected"
|
||||
tools:ignore="TouchTargetSizeCheck" />
|
||||
|
||||
</RadioGroup>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/layout_device_address"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="15dp"
|
||||
android:layout_marginEnd="@dimen/config_margin_10dp"
|
||||
android:orientation="vertical"
|
||||
android:visibility="gone">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="top"
|
||||
android:orientation="horizontal"
|
||||
tools:ignore="UselessParent">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/specified_device"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<com.xuexiang.xui.widget.edittext.materialedittext.MaterialEditText
|
||||
android:id="@+id/et_device_address"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="5dp"
|
||||
android:layout_weight="1"
|
||||
android:hint="@string/mac_hint"
|
||||
android:singleLine="true"
|
||||
app:met_clearButton="true"
|
||||
app:met_errorMessage="@string/mac_error"
|
||||
app:met_regexp="@string/mac_regex"
|
||||
app:met_validateOnFocusLost="true" />
|
||||
|
||||
<com.xuexiang.xui.widget.button.shadowbutton.RippleShadowShadowButton
|
||||
android:id="@+id/btn_start_discovery"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="5dp"
|
||||
android:gravity="center"
|
||||
android:padding="5dp"
|
||||
android:text="@string/start_discovery"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="@dimen/text_size_mini"
|
||||
app:sb_color_unpressed="@color/colorPrimary"
|
||||
app:sb_ripple_color="@color/white"
|
||||
app:sb_ripple_duration="500"
|
||||
app:sb_shape_type="rectangle"
|
||||
tools:ignore="SmallSp" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/recycler_devices"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="@dimen/config_margin_5dp" />
|
||||
|
||||
</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.Spacing"
|
||||
android:drawableStart="@drawable/ic_delete"
|
||||
android:text="@string/discard"
|
||||
tools:ignore="RtlSymmetry,TextContrastCheck,TouchTargetSizeCheck" />
|
||||
|
||||
<com.xuexiang.xui.widget.textview.supertextview.SuperButton
|
||||
android:id="@+id/btn_save"
|
||||
style="@style/SuperButton.Blue.Icon.Spacing"
|
||||
android:drawableStart="@drawable/ic_save"
|
||||
android:text="@string/submit"
|
||||
tools:ignore="RtlSymmetry,TextContrastCheck,TouchTargetSizeCheck" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
@ -1063,8 +1063,8 @@
|
||||
<string name="ip_hint">Broadcast Address, eg. 192.168.1.255</string>
|
||||
<string name="ip_error">Malformed IP address, eg. 192.168.168.168</string>
|
||||
<string name="ip_regex" tools:ignore="TypographyDashes">^((\\d|[1-9]\\d|1\\d\\d|2[0-4]\\d|25[0-5])\.){3}(\\d|[1-9]\\d|1\\d\\d|2[0-4]\\d|25[0-5])$</string>
|
||||
<string name="mac_hint">Network card mac, eg. AA:BB:CC:DD:EE:FF</string>
|
||||
<string name="mac_error">The network card mac format is incorrect, eg. AA:BB:CC:DD:EE:FF</string>
|
||||
<string name="mac_hint">Required, eg. AA:BB:CC:DD:EE:FF</string>
|
||||
<string name="mac_error">Mac format is incorrect, eg. AA:BB:CC:DD:EE:FF</string>
|
||||
<string name="mac_regex" tools:ignore="TypographyDashes">^((([a-fA-F0-9]{2}:){5})|(([a-fA-F0-9]{2}-){5}))[a-fA-F0-9]{2}$</string>
|
||||
<string name="broadcast_address">Broadcast Address</string>
|
||||
<string name="ip">IP</string>
|
||||
@ -1155,6 +1155,12 @@
|
||||
<string name="meter">m</string>
|
||||
<string name="uid">UID</string>
|
||||
|
||||
<string name="enable_bluetooth">Enable Bluetooth discovery</string>
|
||||
<string name="enable_bluetooth_dialog">Bluetooth device discovery service must be enabled to proceed with retrieval!\nEnable now?</string>
|
||||
<string name="enable_bluetooth_tips">To support features like automatic tasks that require Bluetooth discovery</string>
|
||||
<string name="scan_interval">Scan Interval</string>
|
||||
<string name="ignore_anonymous">Ignore Anonymous</string>
|
||||
|
||||
<string name="task_name_status">Name/Status</string>
|
||||
<string name="task_name">Task Name</string>
|
||||
<string name="task_description">Description</string>
|
||||
@ -1400,10 +1406,26 @@
|
||||
<string name="alarm_volume">Alarm Volume</string>
|
||||
<string name="alarm_play_times">Play Times(0=Infinite)</string>
|
||||
|
||||
<string name="invalid_tag">%s tag is invalid: %s</string>
|
||||
<string name="invalid_tag" formatted="false">%s tag is invalid: %s</string>
|
||||
<string name="invalid_task_name">Please input task name.</string>
|
||||
<string name="invalid_conditions">Please add trigger conditions.</string>
|
||||
<string name="invalid_actions">Please add execution actions.</string>
|
||||
<string name="invalid_cron">Please set the time for the scheduled task</string>
|
||||
<string name="invalid_proxy_host">Proxy server hostname resolution failed: proxyHost=%s</string>
|
||||
|
||||
<string name="bluetooth_state_changed">Bluetooth State Changed</string>
|
||||
<string name="specified_state">Spec. St.</string>
|
||||
<string name="state_on">On</string>
|
||||
<string name="state_off">Off</string>
|
||||
<string name="bluetooth_discovery_finished">Bluetooth Device Discovery Finished</string>
|
||||
<string name="specified_result">Spec. Res.</string>
|
||||
<string name="discovered">Discovered</string>
|
||||
<string name="undiscovered">Undiscovered</string>
|
||||
<string name="bluetooth_acl_connected">Bluetooth Device Connected</string>
|
||||
<string name="bluetooth_acl_disconnected">Bluetooth Device Disconnected</string>
|
||||
<string name="specified_device">Spec. Dev.</string>
|
||||
<string name="device_address_hint">Bluetooth Device MAC Address</string>
|
||||
<string name="bluetooth_not_supported">Bluetooth not supported.</string>
|
||||
<string name="start_discovery">Discovery</string>
|
||||
<string name="invalid_bluetooth_mac_address">Bluetooth Mac Address is invalid, eg. AA:BB:CC:DD:EE:FF</string>
|
||||
</resources>
|
||||
|
@ -168,6 +168,7 @@
|
||||
<item name="android:layout_marginTop">5dp</item>
|
||||
<item name="android:layout_width">match_parent</item>
|
||||
<item name="android:layout_height">wrap_content</item>
|
||||
<item name="android:minHeight">30dp</item>
|
||||
<item name="android:singleLine">true</item>
|
||||
<!--<item name="android:textSize">@dimen/text_size_small</item>-->
|
||||
<item name="android:button">@drawable/custom_radio_button</item>
|
||||
|
@ -1064,8 +1064,8 @@
|
||||
<string name="ip_hint">可选,内网广播地址,例如:192.168.1.255</string>
|
||||
<string name="ip_error">IP地址格式错误,例如:192.168.168.168</string>
|
||||
<string name="ip_regex" tools:ignore="TypographyDashes">^((\\d|[1-9]\\d|1\\d\\d|2[0-4]\\d|25[0-5])\.){3}(\\d|[1-9]\\d|1\\d\\d|2[0-4]\\d|25[0-5])$</string>
|
||||
<string name="mac_hint">必填,网卡mac,例如:AA:BB:CC:DD:EE:FF</string>
|
||||
<string name="mac_error">网卡mac格式错误,例如:AA:BB:CC:DD:EE:FF</string>
|
||||
<string name="mac_hint">必填,例如:AA:BB:CC:DD:EE:FF</string>
|
||||
<string name="mac_error">格式错误,例如:AA:BB:CC:DD:EE:FF</string>
|
||||
<string name="mac_regex" tools:ignore="TypographyDashes">^((([a-fA-F0-9]{2}:){5})|(([a-fA-F0-9]{2}-){5}))[a-fA-F0-9]{2}$</string>
|
||||
<string name="broadcast_address">内网广播地址</string>
|
||||
<string name="ip">IP</string>
|
||||
@ -1156,6 +1156,12 @@
|
||||
<string name="meter">米</string>
|
||||
<string name="uid">UID</string>
|
||||
|
||||
<string name="enable_bluetooth">发现蓝牙设备服务</string>
|
||||
<string name="enable_bluetooth_dialog">必须开启发现蓝牙设备服务,才能使用获取!\n是否立即启用?</string>
|
||||
<string name="enable_bluetooth_tips">以便支持 自动任务 等需要发现蓝牙的功能</string>
|
||||
<string name="scan_interval">扫描间隔</string>
|
||||
<string name="ignore_anonymous">忽略匿名设备</string>
|
||||
|
||||
<string name="task_name_status">任务名称/状态</string>
|
||||
<string name="task_name">任务名称</string>
|
||||
<string name="task_description">任务描述</string>
|
||||
@ -1401,10 +1407,26 @@
|
||||
<string name="alarm_volume">播放音量</string>
|
||||
<string name="alarm_play_times">播放次数(0=无限)</string>
|
||||
|
||||
<string name="invalid_tag">%s 标签无效:%s</string>
|
||||
<string name="invalid_tag" formatted="false">%s 标签无效:%s</string>
|
||||
<string name="invalid_task_name">请输入任务名称</string>
|
||||
<string name="invalid_conditions">请添加触发条件</string>
|
||||
<string name="invalid_actions">请添加执行动作</string>
|
||||
<string name="invalid_cron">请设置定时任务的时间</string>
|
||||
<string name="invalid_proxy_host">代理服务器主机名解析失败:proxyHost=%s</string>
|
||||
|
||||
<string name="bluetooth_state_changed">蓝牙状态变化</string>
|
||||
<string name="specified_state">指定状态</string>
|
||||
<string name="state_on">已打开</string>
|
||||
<string name="state_off">已关闭</string>
|
||||
<string name="bluetooth_discovery_finished">蓝牙设备搜索完成</string>
|
||||
<string name="specified_result">指定结果</string>
|
||||
<string name="discovered">已发现</string>
|
||||
<string name="undiscovered">未发现</string>
|
||||
<string name="bluetooth_acl_connected">蓝牙设备已连接</string>
|
||||
<string name="bluetooth_acl_disconnected">蓝牙设备已断开</string>
|
||||
<string name="specified_device">指定设备</string>
|
||||
<string name="device_address_hint">蓝牙设备MAC地址</string>
|
||||
<string name="bluetooth_not_supported">不支持蓝牙设备</string>
|
||||
<string name="start_discovery">搜索设备</string>
|
||||
<string name="invalid_bluetooth_mac_address">蓝牙设备MAC地址无效,例如:AA:BB:CC:DD:EE:FF</string>
|
||||
</resources>
|
||||
|
@ -1064,8 +1064,8 @@
|
||||
<string name="ip_hint">可選,內網廣播地址,例如:192.168.1.255</string>
|
||||
<string name="ip_error">IP地址格式錯誤,例如:192.168.168.168</string>
|
||||
<string name="ip_regex" tools:ignore="TypographyDashes">^((\\d|[1-9]\\d|1\\d\\d|2[0-4]\\d|25[0-5])\\.){3}(\\d|[1-9]\\d|1\\d\\d|2[0-4]\\d|25[0-5])$</string>
|
||||
<string name="mac_hint">必填,網卡mac,例如:AA:BB:CC:DD:EE:FF</string>
|
||||
<string name="mac_error">網卡mac格式錯誤,例如:AA:BB:CC:DD:EE:FF</string>
|
||||
<string name="mac_hint">必填,例如:AA:BB:CC:DD:EE:FF</string>
|
||||
<string name="mac_error">格式錯誤,例如:AA:BB:CC:DD:EE:FF</string>
|
||||
<string name="mac_regex" tools:ignore="TypographyDashes">^((([a-fA-F0-9]{2}:){5})|(([a-fA-F0-9]{2}-){5}))[a-fA-F0-9]{2}$</string>
|
||||
<string name="broadcast_address">內網廣播地址</string>
|
||||
<string name="ip">IP</string>
|
||||
@ -1156,6 +1156,12 @@
|
||||
<string name="meter">米</string>
|
||||
<string name="uid">UID</string>
|
||||
|
||||
<string name="enable_bluetooth">啟用藍牙發現</string>
|
||||
<string name="enable_bluetooth_dialog">必須啟用藍牙裝置發現服務才能繼續獲取!\n現在啟用嗎?</string>
|
||||
<string name="enable_bluetooth_tips">以支援自動任務等需要藍牙發現功能的功能</string>
|
||||
<string name="scan_interval">掃描間隔</string>
|
||||
<string name="ignore_anonymous">忽略匿名裝置</string>
|
||||
|
||||
<string name="task_name_status">任務名稱/狀態</string>
|
||||
<string name="task_name">任務名稱</string>
|
||||
<string name="task_description">任務描述</string>
|
||||
@ -1401,10 +1407,26 @@
|
||||
<string name="alarm_volume">播放音量</string>
|
||||
<string name="alarm_play_times">播放次數(0=無限)</string>
|
||||
|
||||
<string name="invalid_tag">%s 標籤無效:%s</string>
|
||||
<string name="invalid_tag" formatted="false">%s 標籤無效:%s</string>
|
||||
<string name="invalid_task_name">請輸入任務名稱</string>
|
||||
<string name="invalid_conditions">請添加觸發條件</string>
|
||||
<string name="invalid_actions">請添加執行動作</string>
|
||||
<string name="invalid_cron">請設置定時任務的時間</string>
|
||||
<string name="invalid_proxy_host">代理伺服器主機名解析失敗:proxyHost=%s</string>
|
||||
|
||||
<string name="bluetooth_state_changed">藍牙狀態變化</string>
|
||||
<string name="specified_state">指定狀態</string>
|
||||
<string name="state_on">已開啟</string>
|
||||
<string name="state_off">已關閉</string>
|
||||
<string name="bluetooth_discovery_finished">藍牙裝置搜索完成</string>
|
||||
<string name="specified_result">指定結果</string>
|
||||
<string name="discovered">已發現</string>
|
||||
<string name="undiscovered">未發現</string>
|
||||
<string name="bluetooth_acl_connected">藍牙裝置已連接</string>
|
||||
<string name="bluetooth_acl_disconnected">藍牙裝置已斷開</string>
|
||||
<string name="specified_device">指定裝置</string>
|
||||
<string name="device_address_hint">藍牙裝置MAC地址</string>
|
||||
<string name="bluetooth_not_supported">不支援藍牙裝置</string>
|
||||
<string name="start_discovery">搜索裝置</string>
|
||||
<string name="invalid_bluetooth_mac_address">藍牙裝置MAC地址無效,例如:AA:BB:CC:DD:EE:FF</string>
|
||||
</resources>
|
||||
|
@ -1064,8 +1064,8 @@
|
||||
<string name="ip_hint">可选,内网广播地址,例如:192.168.1.255</string>
|
||||
<string name="ip_error">IP地址格式错误,例如:192.168.168.168</string>
|
||||
<string name="ip_regex" tools:ignore="TypographyDashes">^((\\d|[1-9]\\d|1\\d\\d|2[0-4]\\d|25[0-5])\.){3}(\\d|[1-9]\\d|1\\d\\d|2[0-4]\\d|25[0-5])$</string>
|
||||
<string name="mac_hint">必填,网卡mac,例如:AA:BB:CC:DD:EE:FF</string>
|
||||
<string name="mac_error">网卡mac格式错误,例如:AA:BB:CC:DD:EE:FF</string>
|
||||
<string name="mac_hint">必填,例如:AA:BB:CC:DD:EE:FF</string>
|
||||
<string name="mac_error">格式错误,例如:AA:BB:CC:DD:EE:FF</string>
|
||||
<string name="mac_regex" tools:ignore="TypographyDashes">^((([a-fA-F0-9]{2}:){5})|(([a-fA-F0-9]{2}-){5}))[a-fA-F0-9]{2}$</string>
|
||||
<string name="broadcast_address">内网广播地址</string>
|
||||
<string name="ip">IP</string>
|
||||
@ -1156,6 +1156,12 @@
|
||||
<string name="meter">米</string>
|
||||
<string name="uid">UID</string>
|
||||
|
||||
<string name="enable_bluetooth">发现蓝牙设备服务</string>
|
||||
<string name="enable_bluetooth_dialog">必须开启发现蓝牙设备服务,才能使用获取!\n是否立即启用?</string>
|
||||
<string name="enable_bluetooth_tips">以便支持 自动任务 等需要发现蓝牙的功能</string>
|
||||
<string name="scan_interval">扫描间隔</string>
|
||||
<string name="ignore_anonymous">忽略匿名设备</string>
|
||||
|
||||
<string name="task_name_status">任务名称/状态</string>
|
||||
<string name="task_name">任务名称</string>
|
||||
<string name="task_description">任务描述</string>
|
||||
@ -1401,10 +1407,26 @@
|
||||
<string name="alarm_volume">播放音量</string>
|
||||
<string name="alarm_play_times">播放次数(0=无限)</string>
|
||||
|
||||
<string name="invalid_tag">%s 标签无效:%s</string>
|
||||
<string name="invalid_tag" formatted="false">%s 标签无效:%s</string>
|
||||
<string name="invalid_task_name">请输入任务名称</string>
|
||||
<string name="invalid_conditions">请添加触发条件</string>
|
||||
<string name="invalid_actions">请添加执行动作</string>
|
||||
<string name="invalid_cron">请设置定时任务的时间</string>
|
||||
<string name="invalid_proxy_host">代理服务器主机名解析失败:proxyHost=%s</string>
|
||||
|
||||
<string name="bluetooth_state_changed">蓝牙状态变化</string>
|
||||
<string name="specified_state">指定状态</string>
|
||||
<string name="state_on">已打开</string>
|
||||
<string name="state_off">已关闭</string>
|
||||
<string name="bluetooth_discovery_finished">蓝牙设备搜索完成</string>
|
||||
<string name="specified_result">指定结果</string>
|
||||
<string name="discovered">已发现</string>
|
||||
<string name="undiscovered">未发现</string>
|
||||
<string name="bluetooth_acl_connected">蓝牙设备已连接</string>
|
||||
<string name="bluetooth_acl_disconnected">蓝牙设备已断开</string>
|
||||
<string name="specified_device">指定设备</string>
|
||||
<string name="device_address_hint">蓝牙设备MAC地址</string>
|
||||
<string name="bluetooth_not_supported">不支持蓝牙设备</string>
|
||||
<string name="start_discovery">搜索设备</string>
|
||||
<string name="invalid_bluetooth_mac_address">蓝牙设备MAC地址无效,例如:AA:BB:CC:DD:EE:FF</string>
|
||||
</resources>
|
||||
|
@ -170,6 +170,7 @@
|
||||
<item name="android:layout_marginTop">5dp</item>
|
||||
<item name="android:layout_width">match_parent</item>
|
||||
<item name="android:layout_height">wrap_content</item>
|
||||
<item name="android:minHeight">30dp</item>
|
||||
<item name="android:singleLine">true</item>
|
||||
<!--<item name="android:textSize">@dimen/text_size_small</item>-->
|
||||
<item name="android:button">@drawable/custom_radio_button</item>
|
||||
|
Loading…
x
Reference in New Issue
Block a user