From 04d8c9015a20b6fe3a3233dcecd9d7674ff8b391 Mon Sep 17 00:00:00 2001
From: pppscn <35696959@qq.com>
Date: Sun, 5 Feb 2023 11:56:55 +0800
Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=EF=BC=9A=E6=9D=A5=E7=94=B5?=
=?UTF-8?q?=E8=BD=AC=E5=8F=91=E9=80=BB=E8=BE=91=20&=20=E6=96=B0=E5=A2=9E?=
=?UTF-8?q?=E6=8F=90=E9=86=92=E7=B1=BB=E5=9E=8B=EF=BC=881.=E6=9D=A5?=
=?UTF-8?q?=E7=94=B5=E6=8C=82=E6=9C=BA=202.=E5=8E=BB=E7=94=B5=E6=8C=82?=
=?UTF-8?q?=E6=9C=BA=203.=E6=9C=AA=E6=8E=A5=E6=9D=A5=E7=94=B5=204.?=
=?UTF-8?q?=E6=9D=A5=E7=94=B5=E6=8F=90=E9=86=92=205.=E6=9D=A5=E7=94=B5?=
=?UTF-8?q?=E6=8E=A5=E9=80=9A=206.=E5=8E=BB=E7=94=B5=E6=8B=A8=E5=87=BA?=
=?UTF-8?q?=EF=BC=89?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
app/src/main/AndroidManifest.xml | 3 +-
.../idormy/sms/forwarder/entity/CallInfo.kt | 2 +-
.../idormy/sms/forwarder/entity/CloneInfo.kt | 168 +++++++-------
.../forwarder/fragment/SettingsFragment.kt | 32 ++-
.../sms/forwarder/receiver/CallReceiver.kt | 151 ++++++++++++
.../forwarder/receiver/PhoneStateReceiver.kt | 217 ++++++------------
.../idormy/sms/forwarder/utils/Constants.kt | 2 +
.../sms/forwarder/utils/HttpServerUtils.kt | 6 +
.../idormy/sms/forwarder/utils/PhoneUtils.kt | 17 +-
.../sms/forwarder/utils/SettingUtils.kt | 10 +-
app/src/main/res/layout/fragment_settings.xml | 160 ++++++++-----
app/src/main/res/values-en/strings.xml | 16 +-
app/src/main/res/values/strings.xml | 9 +-
13 files changed, 491 insertions(+), 302 deletions(-)
create mode 100644 app/src/main/java/com/idormy/sms/forwarder/receiver/CallReceiver.kt
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 6f0a318c..004f5d80 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -242,11 +242,12 @@
+
diff --git a/app/src/main/java/com/idormy/sms/forwarder/entity/CallInfo.kt b/app/src/main/java/com/idormy/sms/forwarder/entity/CallInfo.kt
index 95348543..80aae874 100644
--- a/app/src/main/java/com/idormy/sms/forwarder/entity/CallInfo.kt
+++ b/app/src/main/java/com/idormy/sms/forwarder/entity/CallInfo.kt
@@ -13,7 +13,7 @@ data class CallInfo(
var dateLong: Long = 0L,
//获取通话时长,值为多少秒
var duration: Int = 0,
- //通话类型:1=呼入, 2=呼出, 3=未接, 4=未接提醒
+ //通话类型:1.来电挂机 2.去电挂机 3.未接来电 4.来电提醒 5.来电接通 6.去电拨出
var type: Int = 1,
//被呼号码
@SerializedName("via_number")
diff --git a/app/src/main/java/com/idormy/sms/forwarder/entity/CloneInfo.kt b/app/src/main/java/com/idormy/sms/forwarder/entity/CloneInfo.kt
index 36dbf475..5763d38f 100644
--- a/app/src/main/java/com/idormy/sms/forwarder/entity/CloneInfo.kt
+++ b/app/src/main/java/com/idormy/sms/forwarder/entity/CloneInfo.kt
@@ -1,82 +1,88 @@
-package com.idormy.sms.forwarder.entity
-
-import com.google.gson.annotations.SerializedName
-import com.idormy.sms.forwarder.database.entity.Frpc
-import com.idormy.sms.forwarder.database.entity.Rule
-import com.idormy.sms.forwarder.database.entity.Sender
-import java.io.Serializable
-
-data class CloneInfo(
- @SerializedName("version_code")
- var versionCode: Int = 0,
- @SerializedName("version_name")
- var versionName: String? = null,
- @SerializedName("enable_sms")
- var enableSms: Boolean = false,
- @SerializedName("enable_phone")
- var enablePhone: Boolean = false,
- @SerializedName("call_type1")
- var callType1: Boolean = false,
- @SerializedName("call_type2")
- var callType2: Boolean = false,
- @SerializedName("call_type3")
- var callType3: Boolean = false,
- @SerializedName("enable_app_notify")
- var enableAppNotify: Boolean = false,
- @SerializedName("cancel_app_notify")
- var cancelAppNotify: Boolean = false,
- @SerializedName("enable_not_user_present")
- var enableNotUserPresent: Boolean = false,
- @SerializedName("enable_load_app_list")
- var enableLoadAppList: Boolean = false,
- @SerializedName("enable_load_user_app_list")
- var enableLoadUserAppList: Boolean = false,
- @SerializedName("enable_load_system_app_list")
- var enableLoadSystemAppList: Boolean = false,
- @SerializedName("duplicate_messages_limits")
- var duplicateMessagesLimits: Int = 0,
- @SerializedName("enable_battery_receiver")
- var enableBatteryReceiver: Boolean = false,
- @SerializedName("battery_level_min")
- var batteryLevelMin: Int = 0,
- @SerializedName("battery_level_max")
- var batteryLevelMax: Int = 0,
- @SerializedName("battery_level_once")
- var batteryLevelOnce: Boolean = false,
- @SerializedName("enable_battery_cron")
- var enableBatteryCron: Boolean = false,
- @SerializedName("battery_cron_start_time")
- var batteryCronStartTime: String? = null,
- @SerializedName("battery_cron_interval")
- var batteryCronInterval: Int = 0,
- @SerializedName("enable_exclude_from_recents")
- var enableExcludeFromRecents: Boolean = false,
- @SerializedName("enable_cactus")
- var enableCactus: Boolean = false,
- @SerializedName("enable_play_silence_music")
- var enablePlaySilenceMusic: Boolean = false,
- @SerializedName("enable_one_pixel_activity")
- var enableOnePixelActivity: Boolean = false,
- @SerializedName("request_retry_times")
- var requestRetryTimes: Int = 0,
- @SerializedName("request_delay_time")
- var requestDelayTime: Int = 0,
- @SerializedName("request_timeout")
- var requestTimeout: Int = 0,
- @SerializedName("notify_content")
- var notifyContent: String? = null,
- @SerializedName("enable_sms_template")
- var enableSmsTemplate: Boolean = false,
- @SerializedName("sms_template")
- var smsTemplate: String? = null,
- @SerializedName("enable_help_tip")
- var enableHelpTip: Boolean = false,
- @SerializedName("enable_pure_client_mode")
- var enablePureClientMode: Boolean = false,
- @SerializedName("sender_list")
- var senderList: List? = null,
- @SerializedName("rule_list")
- var ruleList: List? = null,
- @SerializedName("frpc_list")
- var frpcList: List? = null,
+package com.idormy.sms.forwarder.entity
+
+import com.google.gson.annotations.SerializedName
+import com.idormy.sms.forwarder.database.entity.Frpc
+import com.idormy.sms.forwarder.database.entity.Rule
+import com.idormy.sms.forwarder.database.entity.Sender
+import java.io.Serializable
+
+data class CloneInfo(
+ @SerializedName("version_code")
+ var versionCode: Int = 0,
+ @SerializedName("version_name")
+ var versionName: String? = null,
+ @SerializedName("enable_sms")
+ var enableSms: Boolean = false,
+ @SerializedName("enable_phone")
+ var enablePhone: Boolean = false,
+ @SerializedName("call_type1")
+ var callType1: Boolean = false,
+ @SerializedName("call_type2")
+ var callType2: Boolean = false,
+ @SerializedName("call_type3")
+ var callType3: Boolean = false,
+ @SerializedName("call_type4")
+ var callType4: Boolean = false,
+ @SerializedName("call_type5")
+ var callType5: Boolean = false,
+ @SerializedName("call_type6")
+ var callType6: Boolean = false,
+ @SerializedName("enable_app_notify")
+ var enableAppNotify: Boolean = false,
+ @SerializedName("cancel_app_notify")
+ var cancelAppNotify: Boolean = false,
+ @SerializedName("enable_not_user_present")
+ var enableNotUserPresent: Boolean = false,
+ @SerializedName("enable_load_app_list")
+ var enableLoadAppList: Boolean = false,
+ @SerializedName("enable_load_user_app_list")
+ var enableLoadUserAppList: Boolean = false,
+ @SerializedName("enable_load_system_app_list")
+ var enableLoadSystemAppList: Boolean = false,
+ @SerializedName("duplicate_messages_limits")
+ var duplicateMessagesLimits: Int = 0,
+ @SerializedName("enable_battery_receiver")
+ var enableBatteryReceiver: Boolean = false,
+ @SerializedName("battery_level_min")
+ var batteryLevelMin: Int = 0,
+ @SerializedName("battery_level_max")
+ var batteryLevelMax: Int = 0,
+ @SerializedName("battery_level_once")
+ var batteryLevelOnce: Boolean = false,
+ @SerializedName("enable_battery_cron")
+ var enableBatteryCron: Boolean = false,
+ @SerializedName("battery_cron_start_time")
+ var batteryCronStartTime: String? = null,
+ @SerializedName("battery_cron_interval")
+ var batteryCronInterval: Int = 0,
+ @SerializedName("enable_exclude_from_recents")
+ var enableExcludeFromRecents: Boolean = false,
+ @SerializedName("enable_cactus")
+ var enableCactus: Boolean = false,
+ @SerializedName("enable_play_silence_music")
+ var enablePlaySilenceMusic: Boolean = false,
+ @SerializedName("enable_one_pixel_activity")
+ var enableOnePixelActivity: Boolean = false,
+ @SerializedName("request_retry_times")
+ var requestRetryTimes: Int = 0,
+ @SerializedName("request_delay_time")
+ var requestDelayTime: Int = 0,
+ @SerializedName("request_timeout")
+ var requestTimeout: Int = 0,
+ @SerializedName("notify_content")
+ var notifyContent: String? = null,
+ @SerializedName("enable_sms_template")
+ var enableSmsTemplate: Boolean = false,
+ @SerializedName("sms_template")
+ var smsTemplate: String? = null,
+ @SerializedName("enable_help_tip")
+ var enableHelpTip: Boolean = false,
+ @SerializedName("enable_pure_client_mode")
+ var enablePureClientMode: Boolean = false,
+ @SerializedName("sender_list")
+ var senderList: List? = null,
+ @SerializedName("rule_list")
+ var ruleList: List? = null,
+ @SerializedName("frpc_list")
+ var frpcList: List? = null,
) : Serializable
\ No newline at end of file
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 a12c2966..26796760 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
@@ -80,7 +80,7 @@ class SettingsFragment : BaseFragment(), View.OnClickL
switchEnableSms(binding!!.sbEnableSms)
//转发通话记录
switchEnablePhone(
- binding!!.sbEnablePhone, binding!!.scbCallType1, binding!!.scbCallType2, binding!!.scbCallType3, binding!!.scbCallType4
+ binding!!.sbEnablePhone, binding!!.scbCallType1, binding!!.scbCallType2, binding!!.scbCallType3, binding!!.scbCallType4, binding!!.scbCallType5, binding!!.scbCallType6
)
//转发应用通知
switchEnableAppNotify(
@@ -304,15 +304,17 @@ class SettingsFragment : BaseFragment(), View.OnClickL
//转发通话
@SuppressLint("UseSwitchCompatOrMaterialCode")
fun switchEnablePhone(
- sbEnablePhone: SwitchButton, scbCallType1: SmoothCheckBox, scbCallType2: SmoothCheckBox, scbCallType3: SmoothCheckBox, scbCallType4: SmoothCheckBox
+ sbEnablePhone: SwitchButton, scbCallType1: SmoothCheckBox, scbCallType2: SmoothCheckBox, scbCallType3: SmoothCheckBox, scbCallType4: SmoothCheckBox, scbCallType5: SmoothCheckBox, scbCallType6: SmoothCheckBox
) {
sbEnablePhone.isChecked = SettingUtils.enablePhone
scbCallType1.isChecked = SettingUtils.enableCallType1
scbCallType2.isChecked = SettingUtils.enableCallType2
scbCallType3.isChecked = SettingUtils.enableCallType3
scbCallType4.isChecked = SettingUtils.enableCallType4
+ scbCallType5.isChecked = SettingUtils.enableCallType5
+ scbCallType6.isChecked = SettingUtils.enableCallType6
sbEnablePhone.setOnCheckedChangeListener { _: CompoundButton?, isChecked: Boolean ->
- if (isChecked && !SettingUtils.enableCallType1 && !SettingUtils.enableCallType2 && !SettingUtils.enableCallType3 && !SettingUtils.enableCallType4) {
+ if (isChecked && !SettingUtils.enableCallType1 && !SettingUtils.enableCallType2 && !SettingUtils.enableCallType3 && !SettingUtils.enableCallType4 && !SettingUtils.enableCallType5 && !SettingUtils.enableCallType6) {
XToastUtils.info(R.string.enable_phone_fw_tips)
SettingUtils.enablePhone = false
sbEnablePhone.isChecked = false
@@ -354,7 +356,7 @@ class SettingsFragment : BaseFragment(), View.OnClickL
}
scbCallType1.setOnCheckedChangeListener { _: SmoothCheckBox, isChecked: Boolean ->
SettingUtils.enableCallType1 = isChecked
- if (!isChecked && !SettingUtils.enableCallType1 && !SettingUtils.enableCallType2 && !SettingUtils.enableCallType3 && !SettingUtils.enableCallType4) {
+ if (!isChecked && !SettingUtils.enableCallType1 && !SettingUtils.enableCallType2 && !SettingUtils.enableCallType3 && !SettingUtils.enableCallType4 && !SettingUtils.enableCallType5 && !SettingUtils.enableCallType6) {
XToastUtils.info(R.string.enable_phone_fw_tips)
SettingUtils.enablePhone = false
sbEnablePhone.isChecked = false
@@ -362,7 +364,7 @@ class SettingsFragment : BaseFragment(), View.OnClickL
}
scbCallType2.setOnCheckedChangeListener { _: SmoothCheckBox, isChecked: Boolean ->
SettingUtils.enableCallType2 = isChecked
- if (!isChecked && !SettingUtils.enableCallType1 && !SettingUtils.enableCallType2 && !SettingUtils.enableCallType3 && !SettingUtils.enableCallType4) {
+ if (!isChecked && !SettingUtils.enableCallType1 && !SettingUtils.enableCallType2 && !SettingUtils.enableCallType3 && !SettingUtils.enableCallType4 && !SettingUtils.enableCallType5 && !SettingUtils.enableCallType6) {
XToastUtils.info(R.string.enable_phone_fw_tips)
SettingUtils.enablePhone = false
sbEnablePhone.isChecked = false
@@ -370,7 +372,7 @@ class SettingsFragment : BaseFragment(), View.OnClickL
}
scbCallType3.setOnCheckedChangeListener { _: SmoothCheckBox, isChecked: Boolean ->
SettingUtils.enableCallType3 = isChecked
- if (!isChecked && !SettingUtils.enableCallType1 && !SettingUtils.enableCallType2 && !SettingUtils.enableCallType3 && !SettingUtils.enableCallType4) {
+ if (!isChecked && !SettingUtils.enableCallType1 && !SettingUtils.enableCallType2 && !SettingUtils.enableCallType3 && !SettingUtils.enableCallType4 && !SettingUtils.enableCallType5 && !SettingUtils.enableCallType6) {
XToastUtils.info(R.string.enable_phone_fw_tips)
SettingUtils.enablePhone = false
sbEnablePhone.isChecked = false
@@ -378,7 +380,23 @@ class SettingsFragment : BaseFragment(), View.OnClickL
}
scbCallType4.setOnCheckedChangeListener { _: SmoothCheckBox, isChecked: Boolean ->
SettingUtils.enableCallType4 = isChecked
- if (!isChecked && !SettingUtils.enableCallType1 && !SettingUtils.enableCallType2 && !SettingUtils.enableCallType3 && !SettingUtils.enableCallType4) {
+ if (!isChecked && !SettingUtils.enableCallType1 && !SettingUtils.enableCallType2 && !SettingUtils.enableCallType3 && !SettingUtils.enableCallType4 && !SettingUtils.enableCallType5 && !SettingUtils.enableCallType6) {
+ XToastUtils.info(R.string.enable_phone_fw_tips)
+ SettingUtils.enablePhone = false
+ sbEnablePhone.isChecked = false
+ }
+ }
+ scbCallType5.setOnCheckedChangeListener { _: SmoothCheckBox, isChecked: Boolean ->
+ SettingUtils.enableCallType5 = isChecked
+ if (!isChecked && !SettingUtils.enableCallType1 && !SettingUtils.enableCallType2 && !SettingUtils.enableCallType3 && !SettingUtils.enableCallType4 && !SettingUtils.enableCallType5 && !SettingUtils.enableCallType6) {
+ XToastUtils.info(R.string.enable_phone_fw_tips)
+ SettingUtils.enablePhone = false
+ sbEnablePhone.isChecked = false
+ }
+ }
+ scbCallType6.setOnCheckedChangeListener { _: SmoothCheckBox, isChecked: Boolean ->
+ SettingUtils.enableCallType6 = isChecked
+ if (!isChecked && !SettingUtils.enableCallType1 && !SettingUtils.enableCallType2 && !SettingUtils.enableCallType3 && !SettingUtils.enableCallType4 && !SettingUtils.enableCallType5 && !SettingUtils.enableCallType6) {
XToastUtils.info(R.string.enable_phone_fw_tips)
SettingUtils.enablePhone = false
sbEnablePhone.isChecked = false
diff --git a/app/src/main/java/com/idormy/sms/forwarder/receiver/CallReceiver.kt b/app/src/main/java/com/idormy/sms/forwarder/receiver/CallReceiver.kt
new file mode 100644
index 00000000..c80f7a98
--- /dev/null
+++ b/app/src/main/java/com/idormy/sms/forwarder/receiver/CallReceiver.kt
@@ -0,0 +1,151 @@
+package com.idormy.sms.forwarder.receiver
+
+import android.content.Context
+import android.util.Log
+import androidx.work.OneTimeWorkRequestBuilder
+import androidx.work.WorkManager
+import androidx.work.workDataOf
+import com.google.gson.Gson
+import com.idormy.sms.forwarder.R
+import com.idormy.sms.forwarder.entity.CallInfo
+import com.idormy.sms.forwarder.entity.MsgInfo
+import com.idormy.sms.forwarder.utils.PhoneUtils
+import com.idormy.sms.forwarder.utils.SettingUtils
+import com.idormy.sms.forwarder.utils.Worker
+import com.idormy.sms.forwarder.workers.SendWorker
+import com.xuexiang.xrouter.utils.TextUtils
+import com.xuexiang.xui.utils.ResUtils.getString
+import com.xuexiang.xutil.resource.ResUtils
+import java.util.*
+
+open class CallReceiver : PhoneStateReceiver() {
+
+ companion object {
+ private val TAG = CallReceiver::class.java.simpleName
+
+ //const val ACTION_IN = "android.intent.action.PHONE_STATE"
+ const val ACTION_OUT = "android.intent.action.NEW_OUTGOING_CALL"
+ const val EXTRA_PHONE_NUMBER = "android.intent.extra.PHONE_NUMBER"
+ }
+
+ //来电提醒
+ override fun onIncomingCallReceived(context: Context, number: String?, start: Date) {
+ Log.d(TAG, "onIncomingCallReceived:$number")
+ sendNotice(context, 4, number)
+ }
+
+ //来电接通
+ override fun onIncomingCallAnswered(context: Context, number: String?, start: Date) {
+ Log.d(TAG, "onIncomingCallAnswered:$number")
+ sendNotice(context, 5, number)
+ }
+
+ //来电挂机
+ override fun onIncomingCallEnded(context: Context, number: String?, start: Date, end: Date) {
+ Log.d(TAG, "onIncomingCallEnded:$number")
+ sendCallMsg(context, 1, number)
+ }
+
+ //去电拨出
+ override fun onOutgoingCallStarted(context: Context, number: String?, start: Date) {
+ Log.d(TAG, "onOutgoingCallStarted:$number")
+ sendNotice(context, 6, number)
+ }
+
+ //去电挂机
+ override fun onOutgoingCallEnded(context: Context, number: String?, start: Date, end: Date) {
+ Log.d(TAG, "onOutgoingCallEnded:$number")
+ sendCallMsg(context, 2, number)
+ }
+
+ //未接来电
+ override fun onMissedCall(context: Context, number: String?, start: Date) {
+ Log.d(TAG, "onMissedCall:$number")
+ sendCallMsg(context, 3, number)
+ }
+
+ //转发通话提醒
+ private fun sendNotice(context: Context, callType: Int, phoneNumber: String?) {
+ if (TextUtils.isEmpty(phoneNumber)) return
+
+ //判断是否开启该类型转发
+ if ((callType == 4 && !SettingUtils.enableCallType4) || (callType == 5 && !SettingUtils.enableCallType5) || (callType == 6 && !SettingUtils.enableCallType6)) {
+ Log.w(TAG, "未开启该类型转发,type=$callType")
+ return
+ }
+
+ val contacts = PhoneUtils.getContactByNumber(phoneNumber)
+ val contactName = if (contacts.isNotEmpty()) contacts[0].name else getString(R.string.unknown_number)
+
+ val msg = StringBuilder()
+ msg.append(getString(R.string.linkman)).append(contactName).append("\n")
+ msg.append(getString(R.string.mandatory_type))
+ //通话类型:1.来电挂机 2.去电挂机 3.未接来电 4.来电提醒 5.来电接通 6.去电拨出
+ when (callType) {
+ 1 -> msg.append(ResUtils.getString(R.string.incoming_call_ended))
+ 2 -> msg.append(ResUtils.getString(R.string.outgoing_call_ended))
+ 3 -> msg.append(ResUtils.getString(R.string.missed_call))
+ 4 -> msg.append(ResUtils.getString(R.string.incoming_call_received))
+ 5 -> msg.append(ResUtils.getString(R.string.incoming_call_answered))
+ 6 -> msg.append(ResUtils.getString(R.string.outgoing_call_started))
+ else -> msg.append(ResUtils.getString(R.string.unknown_call))
+ }
+
+ val msgInfo = MsgInfo("call", phoneNumber.toString(), msg.toString(), Date(), "")
+ val request = OneTimeWorkRequestBuilder().setInputData(
+ workDataOf(
+ Worker.sendMsgInfo to Gson().toJson(msgInfo)
+ )
+ ).build()
+ WorkManager.getInstance(context).enqueue(request)
+ }
+
+ //转发通话记录
+ private fun sendCallMsg(context: Context, callType: Int, phoneNumber: String?) {
+ //必须休眠才能获取来电记录,否则可能获取到上一次通话的
+ Thread.sleep(1000)
+
+ //获取后一条通话记录
+ Log.d(TAG, "callType = $callType, phoneNumber = $phoneNumber")
+ val callInfo: CallInfo? = PhoneUtils.getLastCallInfo(callType, phoneNumber)
+ Log.d(TAG, "callInfo = $callInfo")
+ if (callInfo?.number == null) {
+ Log.w(TAG, "查不到通话记录直接发通知")
+ sendNotice(context, callType, phoneNumber)
+ return
+ }
+
+ //判断是否开启该类型转发
+ if ((callInfo.type == 1 && !SettingUtils.enableCallType1) || (callInfo.type == 2 && !SettingUtils.enableCallType2) || (callInfo.type == 3 && !SettingUtils.enableCallType3)) {
+ Log.w(TAG, "未开启该类型转发,type=" + callInfo.type)
+ return
+ }
+
+ //卡槽id:-1=获取失败、0=卡槽1、1=卡槽2
+ val simSlot = callInfo.simId
+ //获取卡槽信息
+ val simInfo = when (simSlot) {
+ 0 -> "SIM1_" + SettingUtils.extraSim1
+ 1 -> "SIM2_" + SettingUtils.extraSim2
+ else -> ""
+ }
+
+ //获取联系人姓名
+ if (TextUtils.isEmpty(callInfo.name)) {
+ val contacts = PhoneUtils.getContactByNumber(phoneNumber)
+ callInfo.name = if (contacts.isNotEmpty()) contacts[0].name else getString(R.string.unknown_number)
+ }
+
+ val msgInfo = MsgInfo(
+ "call", callInfo.number, PhoneUtils.getCallMsg(callInfo), Date(), simInfo, simSlot, callInfo.subId
+ )
+ val request = OneTimeWorkRequestBuilder().setInputData(
+ workDataOf(
+ Worker.sendMsgInfo to Gson().toJson(msgInfo)
+ )
+ ).build()
+ WorkManager.getInstance(context).enqueue(request)
+
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/idormy/sms/forwarder/receiver/PhoneStateReceiver.kt b/app/src/main/java/com/idormy/sms/forwarder/receiver/PhoneStateReceiver.kt
index 4b9ed845..7164b4db 100644
--- a/app/src/main/java/com/idormy/sms/forwarder/receiver/PhoneStateReceiver.kt
+++ b/app/src/main/java/com/idormy/sms/forwarder/receiver/PhoneStateReceiver.kt
@@ -1,178 +1,109 @@
package com.idormy.sms.forwarder.receiver
-import android.Manifest
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
-import android.content.pm.PackageManager
import android.telephony.TelephonyManager
-import android.text.TextUtils
import android.util.Log
-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.R
-import com.idormy.sms.forwarder.entity.CallInfo
-import com.idormy.sms.forwarder.entity.MsgInfo
-import com.idormy.sms.forwarder.utils.*
-import com.idormy.sms.forwarder.workers.SendWorker
-import com.xuexiang.xutil.resource.ResUtils.getString
+import com.idormy.sms.forwarder.utils.SettingUtils
import java.util.*
@Suppress("DEPRECATION")
-class PhoneStateReceiver : BroadcastReceiver() {
+abstract class PhoneStateReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
- try {
- //纯客户端模式
- if (SettingUtils.enablePureClientMode) return
- //总开关
- if (!SettingUtils.enablePhone) return
+ //纯客户端模式
+ if (SettingUtils.enablePureClientMode) return
- //过滤广播
- if (TelephonyManager.ACTION_PHONE_STATE_CHANGED != intent.action) return
+ //总开关
+ if (!SettingUtils.enablePhone) return
- //权限判断
- if (ActivityCompat.checkSelfPermission(
- context, Manifest.permission.READ_PHONE_STATE
- ) != PackageManager.PERMISSION_GRANTED
- ) return
-
- //获取来电号码
- val number = intent.extras!!.getString(TelephonyManager.EXTRA_INCOMING_NUMBER)
+ //We listen to two intents. The new outgoing call only tells us of an outgoing call. We use it to get the number.
+ if (intent.action == CallReceiver.ACTION_OUT) {
+ savedNumber = intent.extras!!.getString(CallReceiver.EXTRA_PHONE_NUMBER)
+ Log.d(TAG, "savedNumber:$savedNumber")
+ } else {
val stateStr = intent.extras!!.getString(TelephonyManager.EXTRA_STATE)
+ val number = intent.extras!!.getString(TelephonyManager.EXTRA_INCOMING_NUMBER)
+ savedNumber = number
+ Log.d(TAG, "stateStr:$stateStr,savedNumber:$savedNumber")
var state = 0
+
when (stateStr) {
TelephonyManager.EXTRA_STATE_IDLE -> state = TelephonyManager.CALL_STATE_IDLE
TelephonyManager.EXTRA_STATE_OFFHOOK -> state = TelephonyManager.CALL_STATE_OFFHOOK
TelephonyManager.EXTRA_STATE_RINGING -> state = TelephonyManager.CALL_STATE_RINGING
}
- Log.d(TAG, "state=$state, number=$number")
- var callSavedNumber: String by SharedPreference("CALL_SAVED_NUMBER", "")
- if (!TextUtils.isEmpty(number)) callSavedNumber = number.toString()
-
- //Incoming call- goes from IDLE to RINGING when it rings, to OFFHOOK when it's answered, to IDLE when its hung up
- //Outgoing call- goes from IDLE to OFFHOOK when it dials out, to IDLE when hung up
- var lastState: Int by SharedPreference("CALL_LAST_STATE", TelephonyManager.CALL_STATE_IDLE)
- if (lastState == state || (state == TelephonyManager.CALL_STATE_RINGING && number == null)) {
- //No change, debounce extras
- Log.d(TAG, "状态没变,防止抖动")
- return
- }
-
- lastState = state
- var callIsIncoming: Boolean by SharedPreference("CALL_IS_INCOMING", false)
- Log.d(TAG, "lastState=$lastState, callIsIncoming=$callIsIncoming, callSavedNumber=$callSavedNumber")
-
- when (state) {
- TelephonyManager.CALL_STATE_RINGING -> {
- Log.d(TAG, "电话响铃")
- callIsIncoming = true
-
- //来电提醒
- if (!TextUtils.isEmpty(number) && SettingUtils.enableCallType4) {
- val contacts = PhoneUtils.getContactByNumber(number)
- val contactName = if (contacts.isNotEmpty()) contacts[0].name else getString(R.string.unknown_number)
-
- val sb = StringBuilder()
- sb.append(getString(R.string.linkman)).append(contactName).append("\n")
- sb.append(getString(R.string.mandatory_type))
- sb.append(getString(R.string.incoming_call))
-
- val msgInfo = MsgInfo("call", number.toString(), sb.toString(), Date(), "", -1)
- val request = OneTimeWorkRequestBuilder().setInputData(
- workDataOf(
- Worker.sendMsgInfo to Gson().toJson(msgInfo)
- )
- ).build()
- WorkManager.getInstance(context).enqueue(request)
- }
- }
- TelephonyManager.CALL_STATE_OFFHOOK ->
- //Transition of ringing->offhook are pickups of incoming calls. Nothing done on them
- callIsIncoming = when {
- lastState != TelephonyManager.CALL_STATE_RINGING -> {
- Log.d(TAG, "去电接通")
- if (!TextUtils.isEmpty(number)) callSavedNumber = number.toString()
- false
- }
- else -> {
- Log.d(TAG, "来电接通")
- true
- }
- }
- TelephonyManager.CALL_STATE_IDLE ->
- //Went to idle- this is the end of a call. What type depends on previous state(s)
- when {
- lastState == TelephonyManager.CALL_STATE_RINGING -> {
- Log.d(TAG, "来电未接")
- sendReceiveCallMsg(context, 3, callSavedNumber)
- callSavedNumber = ""
- }
- callIsIncoming -> {
- Log.d(TAG, "来电挂机")
- sendReceiveCallMsg(context, 1, callSavedNumber)
- callSavedNumber = ""
- }
- else -> {
- Log.d(TAG, "去电挂机")
- sendReceiveCallMsg(context, 2, callSavedNumber)
- callSavedNumber = ""
- }
- }
- }
-
- } catch (e: Exception) {
- Log.e(TAG, e.message.toString())
+ onCallStateChanged(context, state, number)
}
}
- private fun sendReceiveCallMsg(context: Context, callType: Int, phoneNumber: String?) {
- //必须休眠才能获取来电记录,否则可能获取到上一次通话的
- Thread.sleep(500)
- //获取后一条通话记录
- Log.d(TAG, "callType = $callType, phoneNumber = $phoneNumber")
- val callInfo: CallInfo? = PhoneUtils.getLastCallInfo(callType, phoneNumber)
- Log.d(TAG, "callInfo = $callInfo")
- if (callInfo?.number == null) return
+ //Derived classes should override these to respond to specific events of interest
+ protected abstract fun onIncomingCallReceived(context: Context, number: String?, start: Date)
- //判断是否开启该类型转发
- if ((callInfo.type == 1 && !SettingUtils.enableCallType1) || (callInfo.type == 2 && !SettingUtils.enableCallType2) || (callInfo.type == 3 && !SettingUtils.enableCallType3)) {
- Log.w(TAG, "未开启该类型转发,type=" + callInfo.type)
+ protected abstract fun onIncomingCallAnswered(context: Context, number: String?, start: Date)
+
+ protected abstract fun onIncomingCallEnded(context: Context, number: String?, start: Date, end: Date)
+
+ protected abstract fun onOutgoingCallStarted(context: Context, number: String?, start: Date)
+
+ protected abstract fun onOutgoingCallEnded(context: Context, number: String?, start: Date, end: Date)
+
+ protected abstract fun onMissedCall(context: Context, number: String?, start: Date)
+
+ //Deals with actual events
+
+ //Incoming call- goes from IDLE to RINGING when it rings, to OFFHOOK when it's answered, to IDLE when its hung up
+ //Outgoing call- goes from IDLE to OFFHOOK when it dials out, to IDLE when hung up
+ private fun onCallStateChanged(context: Context, state: Int, number: String?) {
+ if (lastState == state || number == null) {
+ //No change, debounce extras
return
}
- //卡槽id:-1=获取失败、0=卡槽1、1=卡槽2
- val simSlot = callInfo.simId
- //获取卡槽信息
- val simInfo = when (simSlot) {
- 0 -> "SIM1_" + SettingUtils.extraSim1
- 1 -> "SIM2_" + SettingUtils.extraSim2
- else -> ""
+ when (state) {
+ TelephonyManager.CALL_STATE_RINGING -> {
+ isIncoming = true
+ callStartTime = Date()
+ savedNumber = number
+
+ onIncomingCallReceived(context, number, callStartTime)
+ }
+ TelephonyManager.CALL_STATE_OFFHOOK ->
+ //Transition of ringing->offhook are pickups of incoming calls. Nothing done on them
+ if (lastState != TelephonyManager.CALL_STATE_RINGING) {
+ isIncoming = false
+ callStartTime = Date()
+
+ onOutgoingCallStarted(context, savedNumber, callStartTime)
+ } else {
+ isIncoming = true
+ callStartTime = Date()
+
+ onIncomingCallAnswered(context, savedNumber, callStartTime)
+ }
+ TelephonyManager.CALL_STATE_IDLE ->
+ //Went to idle- this is the end of a call. What type depends on previous state(s)
+ if (lastState == TelephonyManager.CALL_STATE_RINGING) {
+ //Ring but no pickup
+ onMissedCall(context, savedNumber, callStartTime)
+ } else if (isIncoming) {
+ onIncomingCallEnded(context, savedNumber, callStartTime, Date())
+ } else {
+ onOutgoingCallEnded(context, savedNumber, callStartTime, Date())
+ }
}
-
- //获取联系人姓名
- if (TextUtils.isEmpty(callInfo.name)) {
- val contacts = PhoneUtils.getContactByNumber(phoneNumber)
- callInfo.name = if (contacts.isNotEmpty()) contacts[0].name else getString(R.string.unknown_number)
- }
-
- val msgInfo = MsgInfo(
- "call", callInfo.number, PhoneUtils.getCallMsg(callInfo), Date(), simInfo, simSlot, callInfo.subId
- )
- val request = OneTimeWorkRequestBuilder().setInputData(
- workDataOf(
- Worker.sendMsgInfo to Gson().toJson(msgInfo)
- )
- ).build()
- WorkManager.getInstance(context).enqueue(request)
-
+ lastState = state
}
companion object {
- private const val TAG = "PhoneStateReceiver"
+ private val TAG = PhoneStateReceiver::class.java.simpleName
+
+ //The receiver will be recreated whenever android feels like it. We need a static variable to remember data between instantiations
+ private var lastState = TelephonyManager.CALL_STATE_IDLE
+ private var callStartTime: Date = Date()
+ private var isIncoming: Boolean = false
+ private var savedNumber: String? = null //because the passed incoming is only valid in ringing
}
}
\ No newline at end of file
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 b766fe49..3aefd415 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
@@ -33,6 +33,8 @@ const val SP_ENABLE_CALL_TYPE_1 = "enable_call_type_1"
const val SP_ENABLE_CALL_TYPE_2 = "enable_call_type_2"
const val SP_ENABLE_CALL_TYPE_3 = "enable_call_type_3"
const val SP_ENABLE_CALL_TYPE_4 = "enable_call_type_4"
+const val SP_ENABLE_CALL_TYPE_5 = "enable_call_type_5"
+const val SP_ENABLE_CALL_TYPE_6 = "enable_call_type_6"
const val SP_ENABLE_APP_NOTIFY = "enable_app_notify"
const val SP_ENABLE_CANCEL_APP_NOTIFY = "enable_cancel_app_notify"
diff --git a/app/src/main/java/com/idormy/sms/forwarder/utils/HttpServerUtils.kt b/app/src/main/java/com/idormy/sms/forwarder/utils/HttpServerUtils.kt
index c101bf65..72de5370 100644
--- a/app/src/main/java/com/idormy/sms/forwarder/utils/HttpServerUtils.kt
+++ b/app/src/main/java/com/idormy/sms/forwarder/utils/HttpServerUtils.kt
@@ -139,6 +139,9 @@ class HttpServerUtils private constructor() {
cloneInfo.callType1 = SettingUtils.enableCallType1
cloneInfo.callType2 = SettingUtils.enableCallType2
cloneInfo.callType3 = SettingUtils.enableCallType3
+ cloneInfo.callType4 = SettingUtils.enableCallType4
+ cloneInfo.callType5 = SettingUtils.enableCallType5
+ cloneInfo.callType6 = SettingUtils.enableCallType6
cloneInfo.enableAppNotify = SettingUtils.enableAppNotify
cloneInfo.cancelAppNotify = SettingUtils.enableCancelAppNotify
cloneInfo.enableNotUserPresent = SettingUtils.enableNotUserPresent
@@ -181,6 +184,9 @@ class HttpServerUtils private constructor() {
SettingUtils.enableCallType1 = cloneInfo.callType1
SettingUtils.enableCallType2 = cloneInfo.callType2
SettingUtils.enableCallType3 = cloneInfo.callType3
+ SettingUtils.enableCallType4 = cloneInfo.callType4
+ SettingUtils.enableCallType5 = cloneInfo.callType5
+ SettingUtils.enableCallType6 = cloneInfo.callType6
SettingUtils.enableAppNotify = cloneInfo.enableAppNotify
SettingUtils.enableCancelAppNotify = cloneInfo.cancelAppNotify
SettingUtils.enableNotUserPresent = cloneInfo.enableNotUserPresent
diff --git a/app/src/main/java/com/idormy/sms/forwarder/utils/PhoneUtils.kt b/app/src/main/java/com/idormy/sms/forwarder/utils/PhoneUtils.kt
index b717827e..4bbdb696 100644
--- a/app/src/main/java/com/idormy/sms/forwarder/utils/PhoneUtils.kt
+++ b/app/src/main/java/com/idormy/sms/forwarder/utils/PhoneUtils.kt
@@ -187,11 +187,9 @@ class PhoneUtils private constructor() {
Log.d(TAG, "selectionArgs = $selectionArgs")
//为了兼容性这里全部取出后手动分页
- val cursor = (if (limit == 1) Core.app.contentResolver.query(
- CallLog.Calls.CONTENT_URI, null, selection, selectionArgs.toTypedArray(), CallLog.Calls.DEFAULT_SORT_ORDER + " limit $limit offset $offset"
- ) else Core.app.contentResolver.query(
+ val cursor = Core.app.contentResolver.query(
CallLog.Calls.CONTENT_URI, null, selection, selectionArgs.toTypedArray(), CallLog.Calls.DEFAULT_SORT_ORDER // + " limit $limit offset $offset"
- )) ?: return callInfoList
+ ) ?: return callInfoList
Log.i(TAG, "cursor count:" + cursor.count)
// 避免超过总数后循环取出
@@ -348,12 +346,15 @@ class PhoneUtils private constructor() {
sb.append(callInfo.duration).append("s\n")
}
sb.append(ResUtils.getString(R.string.mandatory_type))
- //通话类型:1.呼入 2.呼出 3.未接 4.来电提醒
+ //通话类型:1.来电挂机 2.去电挂机 3.未接来电 4.来电提醒 5.来电接通 6.去电拨出
when (callInfo.type) {
- 1 -> sb.append(ResUtils.getString(R.string.received_call))
- 2 -> sb.append(ResUtils.getString(R.string.local_outgoing_call))
+ 1 -> sb.append(ResUtils.getString(R.string.incoming_call_ended))
+ 2 -> sb.append(ResUtils.getString(R.string.outgoing_call_ended))
3 -> sb.append(ResUtils.getString(R.string.missed_call))
- else -> sb.append(ResUtils.getString(R.string.incoming_call))
+ 4 -> sb.append(ResUtils.getString(R.string.incoming_call_received))
+ 5 -> sb.append(ResUtils.getString(R.string.incoming_call_answered))
+ 6 -> sb.append(ResUtils.getString(R.string.outgoing_call_started))
+ else -> sb.append(ResUtils.getString(R.string.unknown_call))
}
return sb.toString()
}
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 cbb26182..671bdee2 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
@@ -18,10 +18,10 @@ class SettingUtils private constructor() {
//是否转发通话
var enablePhone: Boolean by SharedPreference(SP_ENABLE_PHONE, false)
- //是否转发通话——已接来电
+ //是否转发通话——来电挂机
var enableCallType1: Boolean by SharedPreference(SP_ENABLE_CALL_TYPE_1, false)
- //是否转发通话——本机去电
+ //是否转发通话——去电挂机
var enableCallType2: Boolean by SharedPreference(SP_ENABLE_CALL_TYPE_2, false)
//是否转发通话——未接来电
@@ -30,6 +30,12 @@ class SettingUtils private constructor() {
//是否转发通话——来电提醒
var enableCallType4: Boolean by SharedPreference(SP_ENABLE_CALL_TYPE_4, false)
+ //是否转发通话——来电接通
+ var enableCallType5: Boolean by SharedPreference(SP_ENABLE_CALL_TYPE_5, false)
+
+ //是否转发通话——去电拨出
+ var enableCallType6: Boolean by SharedPreference(SP_ENABLE_CALL_TYPE_6, false)
+
//是否转发应用通知
var enableAppNotify: Boolean by SharedPreference(SP_ENABLE_APP_NOTIFY, false)
diff --git a/app/src/main/res/layout/fragment_settings.xml b/app/src/main/res/layout/fragment_settings.xml
index acd063e0..04d853ec 100644
--- a/app/src/main/res/layout/fragment_settings.xml
+++ b/app/src/main/res/layout/fragment_settings.xml
@@ -100,76 +100,130 @@
-
-
-
+ android:layout_weight="1"
+ android:orientation="vertical">
-
+
-
+
-
+
-
+
-
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/values-en/strings.xml b/app/src/main/res/values-en/strings.xml
index 46d452c4..b223fca0 100644
--- a/app/src/main/res/values-en/strings.xml
+++ b/app/src/main/res/values-en/strings.xml
@@ -222,6 +222,10 @@
Test PackageName
Test Notify Title
Test Notify Content
+ Run Logic
+ All Run
+ Run Until Fail
+ Run Until Success
SIM Slot
Field
Phone No.
@@ -505,10 +509,13 @@
Ring duration:
Type:
Call type:
+ Incoming Received
+ Incoming Answered
+ Incoming Ended
+ Outgoing Started
+ Outgoing Ended
Missed
- Incoming
- Received
- Call out
+ Unknown
Optional:
Optional:
Active request
@@ -840,6 +847,7 @@
Frpc failed to run
Successfully deleted
[Note] The sending channel has been disabled, and its associated rules will not be sent even if they match!
+ [Note] The sending channel is already in the list, no need to add it again!
Local Call:
Remote SMS:
Clear
@@ -941,4 +949,6 @@
Copy
SM4 Key
Client or server interaction messages are all encrypted and decrypted using SM4
+
+ Del Sender
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 0b625bb0..b8a4a915 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -510,10 +510,13 @@
响铃时长:
类型:
通话类型:
+ 来电提醒
+ 来电接通
+ 来电挂机
+ 去电拨出
+ 去电挂机
未接来电
- 来电提醒
- 已接来电
- 本机去电
+ 未知通话
可选操作:
可选类型:
主动请求