diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index c4dddf2c..f4ccc22c 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -264,31 +264,32 @@ - - - - - - - - - - - - + + + + android:permission="android.permission.READ_PHONE_STATE"> - - + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/java/com/idormy/sms/forwarder/App.kt b/app/src/main/java/com/idormy/sms/forwarder/App.kt index 9c1a3510..d9f88b83 100644 --- a/app/src/main/java/com/idormy/sms/forwarder/App.kt +++ b/app/src/main/java/com/idormy/sms/forwarder/App.kt @@ -6,6 +6,8 @@ import android.app.PendingIntent import android.content.Context import android.content.Intent import android.content.IntentFilter +import android.net.ConnectivityManager +import android.net.wifi.WifiManager import android.os.Build import android.util.Log import androidx.lifecycle.MutableLiveData @@ -21,6 +23,7 @@ import com.idormy.sms.forwarder.database.repository.* import com.idormy.sms.forwarder.entity.SimInfo import com.idormy.sms.forwarder.receiver.BatteryReceiver import com.idormy.sms.forwarder.receiver.CactusReceiver +import com.idormy.sms.forwarder.receiver.NetworkChangeReceiver import com.idormy.sms.forwarder.service.ForegroundService import com.idormy.sms.forwarder.service.HttpServerService import com.idormy.sms.forwarder.utils.* @@ -38,6 +41,7 @@ import java.text.SimpleDateFormat import java.util.* import java.util.concurrent.TimeUnit +@Suppress("DEPRECATION") class App : Application(), CactusCallback, Configuration.Provider by Core { val applicationScope = CoroutineScope(SupervisorJob()) @@ -128,8 +132,18 @@ class App : Application(), CactusCallback, Configuration.Provider by Core { //监听电量&充电状态变化 val batteryReceiver = BatteryReceiver() - val filter = IntentFilter(Intent.ACTION_BATTERY_CHANGED) - registerReceiver(batteryReceiver, filter) + val batteryFilter = IntentFilter(Intent.ACTION_BATTERY_CHANGED) + registerReceiver(batteryReceiver, batteryFilter) + + //监听网络变化 + val networkReceiver = NetworkChangeReceiver() + val networkFilter = IntentFilter().apply { + addAction(ConnectivityManager.CONNECTIVITY_ACTION) + addAction(WifiManager.WIFI_STATE_CHANGED_ACTION) + addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION) + //addAction("android.intent.action.DATA_CONNECTION_STATE_CHANGED") + } + registerReceiver(networkReceiver, networkFilter) //Cactus 集成双进程前台服务,JobScheduler,onePix(一像素),WorkManager,无声音乐 if (SettingUtils.enableCactus) { diff --git a/app/src/main/java/com/idormy/sms/forwarder/entity/task/NetworkSetting.kt b/app/src/main/java/com/idormy/sms/forwarder/entity/task/NetworkSetting.kt index 025a64bf..a538137f 100644 --- a/app/src/main/java/com/idormy/sms/forwarder/entity/task/NetworkSetting.kt +++ b/app/src/main/java/com/idormy/sms/forwarder/entity/task/NetworkSetting.kt @@ -7,9 +7,12 @@ import java.io.Serializable data class NetworkSetting( var description: String = "", //描述 var networkState: Int = 0, //网络状态:0-没有网络,1-移动网络,2-WiFi,3-以太网, 4-未知 + var dataSimSlot: Int = 0, //数据卡槽:0-不限,1-卡1,2-卡2 + var wifiSsid: String = "", //WiFi名称 ) : Serializable { - constructor(networkStateCheckId: Int) : this() { + constructor(networkStateCheckId: Int, dataSimSlotCheckId: Int, ssid: String) : this() { + wifiSsid = ssid networkState = when (networkStateCheckId) { R.id.rb_no_network -> 0 R.id.rb_net_mobile -> 1 @@ -17,6 +20,12 @@ data class NetworkSetting( R.id.rb_net_ethernet -> 3 else -> 4 } + dataSimSlot = when (dataSimSlotCheckId) { + R.id.rb_data_sim_slot_0 -> 0 + R.id.rb_data_sim_slot_1 -> 1 + R.id.rb_data_sim_slot_2 -> 2 + else -> 0 + } description = String.format( getString(R.string.network_state), when (networkState) { @@ -27,6 +36,12 @@ data class NetworkSetting( else -> getString(R.string.net_unknown) } ) + if (networkState == 1 && dataSimSlot != 0) { + description += ", " + getString(R.string.data_sim_index) + ": SIM-" + dataSimSlot + } + if (networkState == 2 && wifiSsid.isNotEmpty()) { + description += ", " + getString(R.string.wifi_ssid) + ": " + wifiSsid + } } fun getNetworkStateCheckId(): Int { @@ -38,4 +53,13 @@ data class NetworkSetting( else -> R.id.rb_net_unknown } } + + fun getDataSimSlotCheckId(): Int { + return when (dataSimSlot) { + 0 -> R.id.rb_data_sim_slot_0 + 1 -> R.id.rb_data_sim_slot_1 + 2 -> R.id.rb_data_sim_slot_2 + else -> R.id.rb_data_sim_slot_0 + } + } } diff --git a/app/src/main/java/com/idormy/sms/forwarder/fragment/SettingsFragment.kt b/app/src/main/java/com/idormy/sms/forwarder/fragment/SettingsFragment.kt index c45561d1..250c4465 100644 --- a/app/src/main/java/com/idormy/sms/forwarder/fragment/SettingsFragment.kt +++ b/app/src/main/java/com/idormy/sms/forwarder/fragment/SettingsFragment.kt @@ -118,9 +118,6 @@ class SettingsFragment : BaseFragment(), View.OnClickL SettingUtils.autoCleanLogsDays = newValue } - //监听网络状态变化 - switchNetworkStateReceiver(binding!!.sbNetworkStateReceiver) - //开机启动 checkWithReboot(binding!!.sbWithReboot, binding!!.tvAutoStartup) //忽略电池优化设置 @@ -607,15 +604,6 @@ class SettingsFragment : BaseFragment(), View.OnClickL } } - //监听网络状态变化 - @SuppressLint("UseSwitchCompatOrMaterialCode") - fun switchNetworkStateReceiver(sbNetworkStateReceiver: SwitchButton) { - sbNetworkStateReceiver.isChecked = SettingUtils.enableNetworkStateReceiver - sbNetworkStateReceiver.setOnCheckedChangeListener { _: CompoundButton?, isChecked: Boolean -> - SettingUtils.enableNetworkStateReceiver = isChecked - } - } - //开机启动 private fun checkWithReboot( @SuppressLint("UseSwitchCompatOrMaterialCode") sbWithReboot: SwitchButton, tvAutoStartup: TextView diff --git a/app/src/main/java/com/idormy/sms/forwarder/fragment/condition/NetworkFragment.kt b/app/src/main/java/com/idormy/sms/forwarder/fragment/condition/NetworkFragment.kt index b54d16ec..73a0ac2f 100644 --- a/app/src/main/java/com/idormy/sms/forwarder/fragment/condition/NetworkFragment.kt +++ b/app/src/main/java/com/idormy/sms/forwarder/fragment/condition/NetworkFragment.kt @@ -69,11 +69,19 @@ class NetworkFragment : BaseFragment(), V } }) + binding!!.rgNetworkState.setOnCheckedChangeListener { _, checkedId -> + Log.d(TAG, "rgNetworkState checkedId:$checkedId") + binding!!.layoutDataSimSlot.visibility = if (checkedId == R.id.rb_net_mobile) View.VISIBLE else View.GONE + binding!!.layoutWifiSsid.visibility = if (checkedId == R.id.rb_net_wifi) View.VISIBLE else View.GONE + } + Log.d(TAG, "initViews eventData:$eventData") if (eventData != null) { val settingVo = Gson().fromJson(eventData, NetworkSetting::class.java) Log.d(TAG, "initViews settingVo:$settingVo") binding!!.rgNetworkState.check(settingVo.getNetworkStateCheckId()) + binding!!.rgDataSimSlot.check(settingVo.getDataSimSlotCheckId()) + binding!!.etWifiSsid.setText(settingVo.wifiSsid) } } @@ -136,6 +144,8 @@ class NetworkFragment : BaseFragment(), V //检查设置 private fun checkSetting(): NetworkSetting { val networkStateCheckId = binding!!.rgNetworkState.checkedRadioButtonId - return NetworkSetting(networkStateCheckId) + val dataSimSlotCheckId = binding!!.rgDataSimSlot.checkedRadioButtonId + val wifiSsid = binding!!.etWifiSsid.text.toString().trim() + return NetworkSetting(networkStateCheckId, dataSimSlotCheckId, wifiSsid) } } \ No newline at end of file diff --git a/app/src/main/java/com/idormy/sms/forwarder/receiver/NetworkChangeReceiver.kt b/app/src/main/java/com/idormy/sms/forwarder/receiver/NetworkChangeReceiver.kt new file mode 100644 index 00000000..4f160978 --- /dev/null +++ b/app/src/main/java/com/idormy/sms/forwarder/receiver/NetworkChangeReceiver.kt @@ -0,0 +1,196 @@ +@file:Suppress("DEPRECATION") + +package com.idormy.sms.forwarder.receiver + +import android.annotation.SuppressLint +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent +import android.net.ConnectivityManager +import android.net.NetworkInfo +import android.net.wifi.WifiManager +import android.os.Build +import android.telephony.SubscriptionInfo +import android.telephony.SubscriptionManager +import android.util.Log +import androidx.annotation.RequiresApi +import androidx.work.OneTimeWorkRequestBuilder +import androidx.work.WorkManager +import androidx.work.workDataOf +import com.idormy.sms.forwarder.utils.TASK_CONDITION_NETWORK +import com.idormy.sms.forwarder.utils.TaskWorker +import com.idormy.sms.forwarder.utils.task.TaskUtils +import com.idormy.sms.forwarder.workers.NetworkWorker + +@Suppress("PrivatePropertyName", "DEPRECATION", "UNUSED_PARAMETER") +class NetworkChangeReceiver : BroadcastReceiver() { + + private val TAG: String = NetworkChangeReceiver::class.java.simpleName + + override fun onReceive(context: Context, intent: Intent) { + Log.d(TAG, "onReceive: ${intent.action}") + when (intent.action) { + ConnectivityManager.CONNECTIVITY_ACTION -> { + handleConnectivityChange(context) + } + + WifiManager.WIFI_STATE_CHANGED_ACTION -> { + handleWifiStateChanged(context, intent) + } + + WifiManager.NETWORK_STATE_CHANGED_ACTION -> { + handleNetworkStateChanged(context, intent) + } + + //"android.intent.action.DATA_CONNECTION_STATE_CHANGED" -> { + // handleDataConnectionStateChanged(context, intent) + //} + } + } + + private fun handleConnectivityChange(context: Context) { + val networkStateOld = TaskUtils.networkState + val dataSimSlotOld = TaskUtils.dataSimSlot + val wifiSsidOld = TaskUtils.wifiSsid + val connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager + val networkInfo = connectivityManager.activeNetworkInfo + if (networkInfo != null && networkInfo.isConnected) { + Log.d(TAG, "Network Connected") + if (networkInfo.type == ConnectivityManager.TYPE_MOBILE) { + //移动网络 + TaskUtils.networkState = 1 + //获取当前使用的 SIM index + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + TaskUtils.dataSimSlot = getSlotIndex(context) + 1 + } + } else if (networkInfo.type == ConnectivityManager.TYPE_WIFI) { + //WiFi网络 + TaskUtils.networkState = 2 + //获取WiFi名称 + val wifiManager = context.applicationContext.getSystemService(Context.WIFI_SERVICE) as WifiManager + val wifiInfo = wifiManager.connectionInfo + TaskUtils.wifiSsid = wifiInfo.ssid.replace("\"", "") + } + } else { + Log.d(TAG, "Network Disconnected") + TaskUtils.networkState = 0 + TaskUtils.dataSimSlot = 0 + TaskUtils.wifiSsid = "" + } + + //网络状态未改变,不执行任务,避免重复通知 + if (networkStateOld == TaskUtils.networkState && dataSimSlotOld == TaskUtils.dataSimSlot && wifiSsidOld == TaskUtils.wifiSsid) { + Log.d(TAG, "Network State Not Changed") + return + } + + //获取公网IP地址后执行任务 + val request = OneTimeWorkRequestBuilder().setInputData( + workDataOf( + TaskWorker.conditionType to TASK_CONDITION_NETWORK, + ) + ).build() + WorkManager.getInstance(context).enqueue(request) + } + + private fun handleWifiStateChanged(context: Context, intent: Intent) { + val wifiState = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE, WifiManager.WIFI_STATE_UNKNOWN) + Log.d(TAG, "WiFi State Changed: $wifiState") + + when (wifiState) { + WifiManager.WIFI_STATE_ENABLED -> { + Log.d(TAG, "WiFi Enabled") + } + + WifiManager.WIFI_STATE_DISABLED -> { + Log.d(TAG, "WiFi Disabled") + } + } + } + + private fun handleNetworkStateChanged(context: Context, intent: Intent) { + val networkInfo = intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO) + if (networkInfo != null && networkInfo.isConnected) { + Log.d(TAG, "Network State Changed: Connected") + } else { + Log.d(TAG, "Network State Changed: Disconnected") + } + } + + //private fun handleDataConnectionStateChanged(context: Context, intent: Intent) { + // val extraData = intent.extras + // val state = extraData?.getString("state") + // val reason = extraData?.getString("reason") + // + // if (state != null && reason != null) { + // Log.d(TAG, "Data Connection State Changed: $state, Reason: $reason") + // } + //} + + // 获取当前数据连接的卡槽ID,不需要判断手机数据流量是否打开(上层已判断) + @RequiresApi(Build.VERSION_CODES.Q) + private fun getSlotIndex(context: Context): Int { + return try { + val subscriptionId = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + SubscriptionManager.getDefaultDataSubscriptionId() + } else { + getDataSubId(context) + } + SubscriptionManager.getSlotIndex(subscriptionId) + } catch (e: Exception) { + e.printStackTrace() + -1 + } + } + + // 获取数据连接的订阅ID + @SuppressLint("DiscouragedPrivateApi") + private fun getDataSubId(context: Context): Int { + val defaultDataSlotId = getDefaultDataSlotId(context) + + return try { + val obj = Class.forName("android.telephony.SubscriptionManager") + .getDeclaredMethod("getSubId", Int::class.javaPrimitiveType) + .invoke(null, defaultDataSlotId) + obj?.let { + when (Build.VERSION.SDK_INT) { + Build.VERSION_CODES.LOLLIPOP -> (it as? LongArray)?.get(0)?.toInt() + else -> (it as? IntArray)?.get(0) + } + } ?: defaultDataSlotId + } catch (e: Exception) { + e.printStackTrace() + defaultDataSlotId + } + } + + // 获取默认数据卡的卡槽ID + private fun getDefaultDataSlotId(context: Context): Int { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) { + val subscriptionManager = SubscriptionManager.from(context.applicationContext) + subscriptionManager?.let { + try { + val subClass = Class.forName(it.javaClass.name) + val getSubID = subClass.getMethod("getDefaultDataSubscriptionInfo") + val subInfo = getSubID.invoke(it) as? SubscriptionInfo + return subInfo?.simSlotIndex ?: -1 + } catch (e: Exception) { + e.printStackTrace() + } + } + } else { + try { + val cls = Class.forName("android.telephony.SubscriptionManager") + val methodName = if (Build.VERSION.SDK_INT == Build.VERSION_CODES.LOLLIPOP) "getSlotId" else "getSlotIndex" + val getSubId = cls.getDeclaredMethod("getDefaultDataSubId") ?: cls.getDeclaredMethod("getDefaultDataSubscriptionId") + val subId = getSubId.invoke(null) as? Int ?: return -1 + val getSlotId = cls.getDeclaredMethod(methodName, Int::class.javaPrimitiveType) + return getSlotId.invoke(null, subId) as? Int ?: -1 + } catch (e: Exception) { + e.printStackTrace() + } + } + return -1 + } + +} diff --git a/app/src/main/java/com/idormy/sms/forwarder/utils/Constants.kt b/app/src/main/java/com/idormy/sms/forwarder/utils/Constants.kt index c59eb647..829d98e5 100644 --- a/app/src/main/java/com/idormy/sms/forwarder/utils/Constants.kt +++ b/app/src/main/java/com/idormy/sms/forwarder/utils/Constants.kt @@ -62,8 +62,6 @@ const val SP_SILENT_PERIOD_START = "silent_period_start" const val SP_SILENT_PERIOD_END = "silent_period_end" const val SP_AUTO_CLEAN_LOGS_DAYS = "auto_clean_logs_days" -const val SP_NET_STATE_RECEIVER = "enable_network_state_receiver" - const val SP_ENABLE_EXCLUDE_FROM_RECENTS = "enable_exclude_from_recents" const val SP_ENABLE_PLAY_SILENCE_MUSIC = "enable_play_silence_music" const val SP_ENABLE_ONE_PIXEL_ACTIVITY = "enable_one_pixel_activity" @@ -577,7 +575,14 @@ var TASK_ACTION_FRAGMENT_LIST = listOf( ), ) +const val SP_BATTERY_INFO = "battery_info" const val SP_BATTERY_STATUS = "battery_status" const val SP_BATTERY_LEVEL = "battery_level" const val SP_BATTERY_PCT = "battery_pct" -const val SP_BATTERY_PLUGGED = "battery_plugged" \ No newline at end of file +const val SP_BATTERY_PLUGGED = "battery_plugged" + +const val SP_NETWORK_STATE = "network_state" +const val SP_DATA_SIM_SLOT = "data_sim_slot" +const val SP_WIFI_SSID = "wifi_ssid" +const val SP_IPV4 = "ipv4" +const val SP_IPV6 = "ipv6" \ No newline at end of file diff --git a/app/src/main/java/com/idormy/sms/forwarder/utils/SettingUtils.kt b/app/src/main/java/com/idormy/sms/forwarder/utils/SettingUtils.kt index a0dae5a3..da6ade74 100644 --- a/app/src/main/java/com/idormy/sms/forwarder/utils/SettingUtils.kt +++ b/app/src/main/java/com/idormy/sms/forwarder/utils/SettingUtils.kt @@ -74,9 +74,6 @@ class SettingUtils private constructor() { //自动删除N天前的转发记录 var autoCleanLogsDays: Int by SharedPreference(SP_AUTO_CLEAN_LOGS_DAYS, 0) - //是否监听网络状态变化 - var enableNetworkStateReceiver: Boolean by SharedPreference(SP_NET_STATE_RECEIVER, false) - //是否不在最近任务列表中显示 var enableExcludeFromRecents: Boolean by SharedPreference(SP_ENABLE_EXCLUDE_FROM_RECENTS, false) diff --git a/app/src/main/java/com/idormy/sms/forwarder/utils/task/TaskUtils.kt b/app/src/main/java/com/idormy/sms/forwarder/utils/task/TaskUtils.kt index 5dd476e1..c684ad48 100644 --- a/app/src/main/java/com/idormy/sms/forwarder/utils/task/TaskUtils.kt +++ b/app/src/main/java/com/idormy/sms/forwarder/utils/task/TaskUtils.kt @@ -1,10 +1,16 @@ package com.idormy.sms.forwarder.utils.task import android.os.BatteryManager +import com.idormy.sms.forwarder.utils.SP_BATTERY_INFO import com.idormy.sms.forwarder.utils.SP_BATTERY_LEVEL import com.idormy.sms.forwarder.utils.SP_BATTERY_PCT 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_DATA_SIM_SLOT +import com.idormy.sms.forwarder.utils.SP_IPV4 +import com.idormy.sms.forwarder.utils.SP_IPV6 +import com.idormy.sms.forwarder.utils.SP_NETWORK_STATE +import com.idormy.sms.forwarder.utils.SP_WIFI_SSID import com.idormy.sms.forwarder.utils.SharedPreference /** @@ -15,7 +21,7 @@ class TaskUtils private constructor() { companion object { //电池信息 - var batteryInfo: String by SharedPreference("batteryInfo", "") + var batteryInfo: String by SharedPreference(SP_BATTERY_INFO, "") //当前电量 var batteryLevel: Int by SharedPreference(SP_BATTERY_LEVEL, 0) @@ -28,5 +34,20 @@ class TaskUtils private constructor() { //充电方式 var batteryPlugged: Int by SharedPreference(SP_BATTERY_PLUGGED, BatteryManager.BATTERY_PLUGGED_AC) + + //网络状态:0-没有网络,1-移动网络,2-WiFi,3-以太网, 4-未知 + var networkState: Int by SharedPreference(SP_NETWORK_STATE, 0) + + //数据卡槽:0-未知,1-卡1,2-卡2 + var dataSimSlot: Int by SharedPreference(SP_DATA_SIM_SLOT, 0) + + //WiFi名称 + var wifiSsid: String by SharedPreference(SP_WIFI_SSID, "") + + //IPv4地址 + var ipv4: String by SharedPreference(SP_IPV4, "") + + //IPv6地址 + var ipv6: String by SharedPreference(SP_IPV6, "") } } \ No newline at end of file diff --git a/app/src/main/java/com/idormy/sms/forwarder/workers/ActionWorker.kt b/app/src/main/java/com/idormy/sms/forwarder/workers/ActionWorker.kt index b4897507..c7170d15 100644 --- a/app/src/main/java/com/idormy/sms/forwarder/workers/ActionWorker.kt +++ b/app/src/main/java/com/idormy/sms/forwarder/workers/ActionWorker.kt @@ -52,51 +52,49 @@ class ActionWorker(context: Context, params: WorkerParameters) : CoroutineWorker var successNum = 0 for (action in actionList) { - when (action.type) { - TASK_ACTION_SENDSMS -> { - val smsSetting = Gson().fromJson(action.setting, SmsSetting::class.java) - if (smsSetting == null) { - Log.d(TAG, "任务$taskId:smsSetting is null") - continue - } - //获取卡槽信息 - if (App.SimInfoList.isEmpty()) { - App.SimInfoList = PhoneUtils.getSimMultiInfo() - } - Log.d(TAG, App.SimInfoList.toString()) + try { + when (action.type) { + TASK_ACTION_SENDSMS -> { + val smsSetting = Gson().fromJson(action.setting, SmsSetting::class.java) + if (smsSetting == null) { + Log.d(TAG, "任务$taskId:smsSetting is null") + continue + } + //获取卡槽信息 + if (App.SimInfoList.isEmpty()) { + App.SimInfoList = PhoneUtils.getSimMultiInfo() + } + Log.d(TAG, App.SimInfoList.toString()) - //发送卡槽: 1=SIM1, 2=SIM2 - val simSlotIndex = smsSetting.simSlot - 1 - //TODO:取不到卡槽信息时,采用默认卡槽发送 - val mSubscriptionId: Int = App.SimInfoList[simSlotIndex]?.mSubscriptionId ?: -1 + //发送卡槽: 1=SIM1, 2=SIM2 + val simSlotIndex = smsSetting.simSlot - 1 + //TODO:取不到卡槽信息时,采用默认卡槽发送 + val mSubscriptionId: Int = App.SimInfoList[simSlotIndex]?.mSubscriptionId ?: -1 - val msg = if (ActivityCompat.checkSelfPermission(XUtil.getContext(), Manifest.permission.SEND_SMS) != PackageManager.PERMISSION_GRANTED) { - ResUtils.getString(R.string.no_sms_sending_permission) - } else { - PhoneUtils.sendSms(mSubscriptionId, smsSetting.phoneNumbers, smsSetting.msgContent) - successNum++ - } + val msg = if (ActivityCompat.checkSelfPermission(XUtil.getContext(), Manifest.permission.SEND_SMS) != PackageManager.PERMISSION_GRANTED) { + ResUtils.getString(R.string.no_sms_sending_permission) + } else { + PhoneUtils.sendSms(mSubscriptionId, smsSetting.phoneNumbers, smsSetting.msgContent) + successNum++ + } - Log.d(TAG, "任务$taskId:send sms result: $msg") - continue - } + Log.d(TAG, "任务$taskId:send sms result: $msg") + } - TASK_ACTION_NOTIFICATION -> { - try { + TASK_ACTION_NOTIFICATION -> { val settingVo = Gson().fromJson(action.setting, Rule::class.java) //自动任务的不需要吐司或者更新日志,特殊处理 logId = -1,msgId = -1 SendUtils.sendMsgSender(msgInfo, settingVo, 0, -1L, -1L) successNum++ - } catch (e: Exception) { - e.printStackTrace() } - continue - } - else -> { - Log.d(TAG, "任务$taskId:action.type is ${action.type}") - continue + else -> { + Log.d(TAG, "任务$taskId:action.type is ${action.type}") + } } + } catch (e: Exception) { + e.printStackTrace() + Log.d(TAG, "任务$taskId:action.type is ${action.type}, exception: ${e.message}") } } diff --git a/app/src/main/java/com/idormy/sms/forwarder/workers/BatteryWorker.kt b/app/src/main/java/com/idormy/sms/forwarder/workers/BatteryWorker.kt index 619ab326..8fb86141 100644 --- a/app/src/main/java/com/idormy/sms/forwarder/workers/BatteryWorker.kt +++ b/app/src/main/java/com/idormy/sms/forwarder/workers/BatteryWorker.kt @@ -39,7 +39,7 @@ class BatteryWorker(context: Context, params: WorkerParameters) : CoroutineWorke return Result.failure() } - val taskList = AppDatabase.getInstance(App.context).taskDao().getByType(TASK_CONDITION_BATTERY) + val taskList = AppDatabase.getInstance(App.context).taskDao().getByType(conditionType) for (task in taskList) { Log.d(TAG, "task = $task") @@ -94,7 +94,7 @@ class BatteryWorker(context: Context, params: WorkerParameters) : CoroutineWorke return Result.failure() } - val taskList = AppDatabase.getInstance(App.context).taskDao().getByType(TASK_CONDITION_CHARGE) + val taskList = AppDatabase.getInstance(App.context).taskDao().getByType(conditionType) for (task in taskList) { Log.d(TAG, "task = $task") @@ -125,7 +125,7 @@ class BatteryWorker(context: Context, params: WorkerParameters) : CoroutineWorke //TODO:判断其他条件是否满足 //TODO: 组装消息体 && 执行具体任务 - val msgInfo = MsgInfo("task", task.name, msg, Date(), task.name) + val msgInfo = MsgInfo("task", task.name, msg, Date(), task.description) val actionData = Data.Builder() .putLong(TaskWorker.taskId, task.id) .putString(TaskWorker.taskActions, task.actions) diff --git a/app/src/main/java/com/idormy/sms/forwarder/workers/NetworkWorker.kt b/app/src/main/java/com/idormy/sms/forwarder/workers/NetworkWorker.kt new file mode 100644 index 00000000..9bb9129c --- /dev/null +++ b/app/src/main/java/com/idormy/sms/forwarder/workers/NetworkWorker.kt @@ -0,0 +1,153 @@ +package com.idormy.sms.forwarder.workers + +import android.content.Context +import android.util.Log +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.App +import com.idormy.sms.forwarder.R +import com.idormy.sms.forwarder.database.AppDatabase +import com.idormy.sms.forwarder.entity.MsgInfo +import com.idormy.sms.forwarder.entity.task.NetworkSetting +import com.idormy.sms.forwarder.entity.task.TaskSetting +import com.idormy.sms.forwarder.utils.PhoneUtils +import com.idormy.sms.forwarder.utils.TaskWorker +import com.idormy.sms.forwarder.utils.task.TaskUtils +import com.xuexiang.xutil.app.ServiceUtils +import com.xuexiang.xutil.resource.ResUtils.getString +import java.net.HttpURLConnection +import java.net.URL +import java.util.Date + +@Suppress("PrivatePropertyName", "DEPRECATION") +class NetworkWorker(context: Context, params: WorkerParameters) : CoroutineWorker(context, params) { + + private val TAG: String = NetworkWorker::class.java.simpleName + private val ipv4Pattern = Regex("^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$") + private val ipv6Pattern = Regex("^([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}$") + + override suspend fun doWork(): Result { + val conditionType = inputData.getInt(TaskWorker.conditionType, -1) + val taskList = AppDatabase.getInstance(App.context).taskDao().getByType(conditionType) + for (task in taskList) { + Log.d(TAG, "task = $task") + + // 根据任务信息执行相应操作 + val conditionList = Gson().fromJson(task.conditions, Array::class.java).toMutableList() + if (conditionList.isEmpty()) { + Log.d(TAG, "任务${task.id}:conditionList is empty") + continue + } + val firstCondition = conditionList.firstOrNull() + if (firstCondition == null) { + Log.d(TAG, "任务${task.id}:firstCondition is null") + continue + } + + val networkSetting = Gson().fromJson(firstCondition.setting, NetworkSetting::class.java) + if (networkSetting == null) { + Log.d(TAG, "任务${task.id}:networkSetting is null") + continue + } + + if (TaskUtils.networkState != networkSetting.networkState) { + Log.d(TAG, "任务${task.id}:networkState is not match, networkSetting = $networkSetting") + continue + } + + //TODO:判断其他条件是否满足 + + var ipv4 = "" + var ipv6 = "" + val msg = StringBuilder() + msg.append(getString(R.string.network_type)).append(": ") + when (networkSetting.networkState) { + //移动网络 + 1 -> { + val dataSimSlot = TaskUtils.dataSimSlot + if (networkSetting.dataSimSlot != 0 && dataSimSlot != networkSetting.dataSimSlot) { + Log.d(TAG, "任务${task.id}:dataSimSlot is not match, networkSetting = $networkSetting") + continue + } + msg.append(getString(R.string.net_mobile)).append("\n") + + if (dataSimSlot != 0) { + msg.append(getString(R.string.data_sim_index)).append(": SIM-").append(dataSimSlot).append("\n") + // 获取 SIM 卡信息 + val simIndex = dataSimSlot - 1 + App.SimInfoList = PhoneUtils.getSimMultiInfo() + if (App.SimInfoList[simIndex]?.mCarrierName != null) { + //获取网络运营商名称:中国移动、中国联通、中国电信 + msg.append(getString(R.string.carrier_name)).append(": ").append(App.SimInfoList[simIndex]?.mCarrierName).append("\n") + } + } + + ipv4 = getPublicIP(false) + ipv6 = getPublicIP(true) + } + + //WiFi + 2 -> { + if (networkSetting.wifiSsid.isNotEmpty() && TaskUtils.wifiSsid != networkSetting.wifiSsid) { + Log.d(TAG, "任务${task.id}:wifiSsid is not match, networkSetting = $networkSetting") + continue + } + msg.append(getString(R.string.net_wifi)).append("\n") + msg.append(getString(R.string.wifi_ssid)).append(": ").append(TaskUtils.wifiSsid).append("\n") + + ipv4 = getPublicIP(false) + ipv6 = getPublicIP(true) + } + + //未知 && 没有网络 + else -> { + msg.append(getString(R.string.no_network)).append("\n") + } + } + + val isHttpServerRunning = ServiceUtils.isServiceRunning("com.idormy.sms.forwarder.service.HttpServerService") + if (ipv4Pattern.matches(ipv4)) { + msg.append(getString(R.string.ipv4)).append(": ").append(ipv4).append("\n") + TaskUtils.ipv4 = ipv4 + if (isHttpServerRunning) { + msg.append(getString(R.string.http_server)).append(": ").append("http://${ipv4}:5000").append("\n") + } + } else { + TaskUtils.ipv4 = "" + } + + if (ipv6Pattern.matches(ipv6)) { + msg.append(getString(R.string.ipv6)).append(": ").append(ipv6).append("\n") + TaskUtils.ipv6 = ipv6 + if (isHttpServerRunning) { + msg.append(getString(R.string.http_server)).append(": ").append("http://[${ipv6}]:5000").append("\n") + } + } else { + TaskUtils.ipv6 = "" + } + + //TODO: 组装消息体 && 执行具体任务 + val msgInfo = MsgInfo("task", task.name, msg.toString().trimEnd(), Date(), task.description) + val actionData = Data.Builder().putLong(TaskWorker.taskId, task.id).putString(TaskWorker.taskActions, task.actions).putString(TaskWorker.msgInfo, Gson().toJson(msgInfo)).build() + val actionRequest = OneTimeWorkRequestBuilder().setInputData(actionData).build() + WorkManager.getInstance().enqueue(actionRequest) + } + + return Result.success() + + } + + //获取公网IP地址 + private fun getPublicIP(ipv6: Boolean = false): String { + val url = if (ipv6) URL("https://api6.ipify.org/") else URL("https://api.ipify.org/") + val urlConnection = url.openConnection() as HttpURLConnection + urlConnection.requestMethod = "GET" + val inputStream = urlConnection.inputStream + return inputStream.bufferedReader().use { it.readText() } + } + +} \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_senders_socket.xml b/app/src/main/res/layout/fragment_senders_socket.xml index 9de65201..1cc4f5d0 100644 --- a/app/src/main/res/layout/fragment_senders_socket.xml +++ b/app/src/main/res/layout/fragment_senders_socket.xml @@ -357,6 +357,7 @@ - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/values-en/strings.xml b/app/src/main/res/values-en/strings.xml index 1c4cb0d9..45c199c6 100644 --- a/app/src/main/res/values-en/strings.xml +++ b/app/src/main/res/values-en/strings.xml @@ -557,9 +557,7 @@ SIM State Monitor [Note] You need to manually create APP forwarding rules, package name: 66666666 Network State Monitor - [Note] You need to manually create APP forwarding rules, package name: 77777777 Network State Change Remind - Send a notification when the network status changes (connection mode/IP change) Keep Alive It is recommended to open the first three switch, do not disable the notification bar, to avoid APP being killed Custom Settings @@ -693,7 +691,7 @@ Logging About - Http Server + HttpServer Start Server Stop Server Server is shutting down. Please wait. @@ -1083,6 +1081,9 @@ Unknown Network State: %s WiFi SSID + If left blank, it won\'t check the connected WiFi SSID. + IPv4 + IPv6 Enable {{LOCATION}} Tag Insert location info into forwarded msg. @@ -1191,4 +1192,8 @@ Absent Ready Unknown + + Any SIM + SIM-1 + SIM-2 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 4406bb98..42b71af3 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -558,9 +558,7 @@ SIM卡槽状态监控 需要手动创建APP转发规则,包名:66666666 网络状态监控 - 需要手动创建APP转发规则,包名:77777777 网络状态改变提醒 - 网络状态改变(连接方式/IP变化)时发出通知 保活措施 建议开启前三项授权或设置,不要禁用通知栏,避免APP被杀 个性设置 @@ -694,7 +692,7 @@ Logging About - Http Server + HttpServer 启动服务 停止服务 Server is shutting down. Please wait. @@ -1084,6 +1082,9 @@ 未知网络 网络状态:%s WiFi名称 + 留空则不判断连接的WiFi-SSID + IPv4 + IPv6 启用 {{定位信息}} 标签 在转发信息中插入手机的当前定位信息 @@ -1192,4 +1193,8 @@ 被移除 已就绪 未知 + + 不限卡槽 + SIM-1 + SIM-2