| | |
| | | } |
| | | } |
| | | } |
| | | |
| | | |
| | | // 通知权限请求 |
| | | private val notificationPermissionRequest = |
| | | private val notificationPermissionRequest = |
| | | registerForActivityResult(ActivityResultContracts.RequestPermission()) { isGranted -> |
| | | if (isGranted) { |
| | | // 权限已授予 |
| | |
| | | |
| | | // 重置提醒计划并检查是否有错过的提醒 |
| | | resetReminders() |
| | | |
| | | |
| | | // 开始权限请求流程 |
| | | startPermissionsFlow() |
| | | } |
| | | |
| | | override fun onResume() { |
| | | super.onResume() |
| | | |
| | | |
| | | // 每次恢复活动时检查短信权限 |
| | | checkAndHandleSmsPermissions(showDialog = false) |
| | | } |
| | | |
| | | |
| | | // 权限请求主流程 |
| | | private fun startPermissionsFlow() { |
| | | // 先请求通知权限,再请求短信权限 |
| | | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { |
| | | // Android 13+ 需要通知权限 |
| | | if (ContextCompat.checkSelfPermission( |
| | | this, |
| | | this, |
| | | Manifest.permission.POST_NOTIFICATIONS |
| | | ) != PackageManager.PERMISSION_GRANTED |
| | | ) { |
| | |
| | | requestSmsPermissions() |
| | | } |
| | | } |
| | | |
| | | |
| | | // 请求短信权限 |
| | | private fun requestSmsPermissions() { |
| | | if (!hasSmsPermissions()) { |
| | |
| | | } |
| | | } |
| | | } |
| | | |
| | | |
| | | // 检查并处理短信权限(可选是否显示对话框) |
| | | private fun checkAndHandleSmsPermissions(showDialog: Boolean = true) { |
| | | if (!hasSmsPermissions()) { |
| | |
| | | } |
| | | } |
| | | } |
| | | |
| | | |
| | | // 检查是否有短信权限 |
| | | private fun hasSmsPermissions(): Boolean { |
| | | return ContextCompat.checkSelfPermission( |
| | | this, Manifest.permission.RECEIVE_SMS |
| | | ) == PackageManager.PERMISSION_GRANTED && |
| | | ContextCompat.checkSelfPermission( |
| | | this, Manifest.permission.READ_SMS |
| | | ) == PackageManager.PERMISSION_GRANTED |
| | | ) == PackageManager.PERMISSION_GRANTED && |
| | | ContextCompat.checkSelfPermission( |
| | | this, Manifest.permission.READ_SMS |
| | | ) == PackageManager.PERMISSION_GRANTED |
| | | } |
| | | |
| | | |
| | | // 显示短信权限解释对话框 |
| | | private fun showSmsPermissionExplanationDialog() { |
| | | AlertDialog.Builder(this) |
| | |
| | | .setCancelable(false) |
| | | .show() |
| | | } |
| | | |
| | | |
| | | // 提供给外部调用的请求通知权限方法 |
| | | fun requestNotificationPermission() { |
| | | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { |
| | | // 判断是否已经有通知权限 |
| | | if (ContextCompat.checkSelfPermission( |
| | | this, |
| | | this, |
| | | Manifest.permission.POST_NOTIFICATIONS |
| | | ) != PackageManager.PERMISSION_GRANTED |
| | | ) { |
| | |
| | | private fun registerSmsReceiver() { |
| | | // 确保不重复注册 |
| | | if (smsReceiver != null) return |
| | | |
| | | |
| | | Log.d("SMS_DEBUG", "MainActivity注册短信接收器") |
| | | smsReceiver = SmsReceiver() |
| | | val filter = IntentFilter(Telephony.Sms.Intents.SMS_RECEIVED_ACTION) |
| | |
| | | company = response.data.details.company, |
| | | pickupCode = response.data.details.pickupCode, |
| | | address = response.data.details.address, |
| | | time = response.data.details.time, |
| | | smsTimestamp = datetime // 添加短信时间戳 |
| | | time = response.data.details.time |
| | | ) |
| | | CodeUtils.saveCode(code) |
| | | } |
| | |
| | | date = response.data.details.date, |
| | | address = response.data.details.address, |
| | | minAmount = response.data.details.min_amount, |
| | | number = response.data.details.number, |
| | | smsTimestamp = datetime // 添加短信时间戳 |
| | | number = response.data.details.number |
| | | ) |
| | | CodeUtils.saveCode(code) |
| | | } |
| | |
| | | amount = response.data.details.amount, |
| | | datetime = response.data.details.datetime, |
| | | address = response.data.details.address, |
| | | balance = response.data.details.balance, |
| | | smsTimestamp = datetime // 添加短信时间戳 |
| | | balance = response.data.details.balance |
| | | ) |
| | | CodeUtils.saveCode(code) |
| | | } |
| | |
| | | end = response.data.details.end, |
| | | seat = response.data.details.seat, |
| | | time = response.data.details.time, |
| | | address = response.data.details.address, |
| | | smsTimestamp = datetime // 添加短信时间戳 |
| | | address = response.data.details.address |
| | | ) |
| | | CodeUtils.saveCode(code) |
| | | } |
| | |
| | | seat = response.data.details.seat, |
| | | time = response.data.details.time, |
| | | address = response.data.details.address, |
| | | trips = response.data.details.trips, |
| | | smsTimestamp = datetime // 添加短信时间戳 |
| | | trips = response.data.details.trips |
| | | ) |
| | | CodeUtils.saveCode(code) |
| | | } |
| | |
| | | SELECT * FROM Code |
| | | WHERE category = :category |
| | | AND code = :code |
| | | AND (smsTimestamp = :smsTimestamp ) |
| | | AND substr(createTime, 1, 10) = substr(:dateString, 1, 10) |
| | | ANd pickup = '0' |
| | | ORDER BY createTime DESC |
| | | LIMIT 1 |
| | | """) |
| | | fun queryByTypeAndCodeAndDate(category: String, code: String, smsTimestamp: Long): Code? |
| | | fun queryByTypeAndCodeAndDate(category: String, code: String, dateString: String): Code? |
| | | |
| | | |
| | | @Query("update Code set pickup = '1' , pickuptime = CURRENT_TIMESTAMP where id=:id") |
| | |
| | | var overTime: String, //快递超期时间、还款日期 |
| | | var address: String, //地址信息 |
| | | var remarks: String, //备注信息保存 |
| | | var smsTimestamp: Long = 0, // 新增短信时间戳字段 |
| | | var time: Date = Date(), |
| | | ) |
| | |
| | | //通话类型:1.来电挂机 2.去电挂机 3.未接来电 4.来电提醒 5.来电接通 6.去电拨出 |
| | | @ColumnInfo(name = "call_type", defaultValue = "0") var callType: Int = 0, |
| | | @ColumnInfo(name = "time") var time: Date = Date(), |
| | | @ColumnInfo(name = "sms_timestamp", defaultValue = "0") var smsTimestamp: Long = 0, |
| | | ) : Parcelable { |
| | | |
| | | val simImageId: Int |
| | |
| | | } |
| | | |
| | | @WorkerThread |
| | | fun queryByTypeAndCodeAndDate(content: String, code: String, smsTimestamp: Long): Code? { |
| | | return codeDao.queryByTypeAndCodeAndDate(content, code, smsTimestamp) |
| | | fun queryByTypeAndCodeAndDate(content: String, code: String, dateString: String): Code? { |
| | | return codeDao.queryByTypeAndCodeAndDate(content, code, dateString) |
| | | } |
| | | |
| | | @WorkerThread |
| | |
| | | |
| | | |
| | | class SmsReceiver : BroadcastReceiver() { |
| | | |
| | | // 添加一个静态同步锁对象 |
| | | companion object { |
| | | private val syncLock = Any() |
| | | } |
| | | // 安全防护关键词数组 |
| | | private var securityKeywordsList = emptyList<String>() |
| | | |
| | |
| | | val messages = arrayOfNulls<SmsMessage>(pdus.size) |
| | | val messageBody = StringBuilder() |
| | | |
| | | // 获取短信时间戳(使用第一条短信的时间戳) |
| | | var timestamp: Long = 0 |
| | | |
| | | for (i in pdus.indices) { |
| | | messages[i] = SmsMessage.createFromPdu(pdus[i] as ByteArray) |
| | | messageBody.append(messages[i]?.messageBody) |
| | | if (i == 0) { |
| | | timestamp = messages[i]?.timestampMillis ?: System.currentTimeMillis() |
| | | } |
| | | } |
| | | |
| | | // 保存原始短信 |
| | |
| | | val sdf = SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault()) |
| | | val createtime = sdf.format(date) |
| | | |
| | | // 根据不同类型处理数据 |
| | | when (response.data.category) { |
| | | "快递" -> { |
| | | val code = CodeUtils.createExpressCode( |
| | | msgId = msgId, |
| | | createTime = createtime, |
| | | post = response.data.details.post, |
| | | company = response.data.details.company, |
| | | pickupCode = response.data.details.pickupCode, |
| | | address = response.data.details.address, |
| | | time = response.data.details.time, |
| | | smsTimestamp = timestamp // 添加短信时间戳 |
| | | ) |
| | | CodeUtils.saveCode(code) |
| | | } |
| | | // 使用同步块处理保存操作 |
| | | synchronized(syncLock) { |
| | | // 根据不同类型处理数据 |
| | | when (response.data.category) { |
| | | "快递" -> { |
| | | val code = CodeUtils.createExpressCode( |
| | | msgId = msgId, |
| | | createTime = createtime, |
| | | post = response.data.details.post, |
| | | company = response.data.details.company, |
| | | pickupCode = response.data.details.pickupCode, |
| | | address = response.data.details.address, |
| | | time = response.data.details.time |
| | | ) |
| | | CodeUtils.saveCode(code) |
| | | } |
| | | |
| | | "还款" -> { |
| | | val code = CodeUtils.createRepaymentCode( |
| | | msgId = msgId, |
| | | createTime = createtime, |
| | | type = response.data.details.type, |
| | | bank = response.data.details.bank, |
| | | amount = response.data.details.amount, |
| | | date = response.data.details.date, |
| | | address = response.data.details.address, |
| | | minAmount = response.data.details.min_amount, |
| | | number = response.data.details.number, |
| | | smsTimestamp = timestamp // 添加短信时间戳 |
| | | ) |
| | | CodeUtils.saveCode(code) |
| | | } |
| | | "还款" -> { |
| | | val code = CodeUtils.createRepaymentCode( |
| | | msgId = msgId, |
| | | createTime = createtime, |
| | | type = response.data.details.type, |
| | | bank = response.data.details.bank, |
| | | amount = response.data.details.amount, |
| | | date = response.data.details.date, |
| | | address = response.data.details.address, |
| | | minAmount = response.data.details.min_amount, |
| | | number = response.data.details.number |
| | | ) |
| | | CodeUtils.saveCode(code) |
| | | } |
| | | |
| | | "收入" -> { |
| | | val code = CodeUtils.createIncomeCode( |
| | | msgId = msgId, |
| | | createTime = createtime, |
| | | bank = response.data.details.bank, |
| | | amount = response.data.details.amount, |
| | | datetime = response.data.details.datetime, |
| | | address = response.data.details.address, |
| | | balance = response.data.details.balance, |
| | | smsTimestamp = timestamp // 添加短信时间戳 |
| | | ) |
| | | CodeUtils.saveCode(code) |
| | | } |
| | | "收入" -> { |
| | | val code = CodeUtils.createIncomeCode( |
| | | msgId = msgId, |
| | | createTime = createtime, |
| | | bank = response.data.details.bank, |
| | | amount = response.data.details.amount, |
| | | datetime = response.data.details.datetime, |
| | | address = response.data.details.address, |
| | | balance = response.data.details.balance |
| | | ) |
| | | CodeUtils.saveCode(code) |
| | | } |
| | | |
| | | "航班" -> { |
| | | val code = CodeUtils.createFlightCode( |
| | | msgId = msgId, |
| | | createTime = createtime, |
| | | company = response.data.details.company, |
| | | start = response.data.details.start, |
| | | end = response.data.details.end, |
| | | seat = response.data.details.seat, |
| | | time = response.data.details.time, |
| | | address = response.data.details.address, |
| | | smsTimestamp = timestamp // 添加短信时间戳 |
| | | ) |
| | | CodeUtils.saveCode(code) |
| | | } |
| | | "航班" -> { |
| | | val code = CodeUtils.createFlightCode( |
| | | msgId = msgId, |
| | | createTime = createtime, |
| | | company = response.data.details.company, |
| | | start = response.data.details.start, |
| | | end = response.data.details.end, |
| | | seat = response.data.details.seat, |
| | | time = response.data.details.time, |
| | | address = response.data.details.address |
| | | ) |
| | | CodeUtils.saveCode(code) |
| | | } |
| | | |
| | | "火车票" -> { |
| | | val code = CodeUtils.createTrainTicketCode( |
| | | msgId = msgId, |
| | | createTime = createtime, |
| | | company = response.data.details.company, |
| | | seat = response.data.details.seat, |
| | | time = response.data.details.time, |
| | | address = response.data.details.address, |
| | | trips = response.data.details.trips, |
| | | smsTimestamp = timestamp // 添加短信时间戳 |
| | | ) |
| | | CodeUtils.saveCode(code) |
| | | "火车票" -> { |
| | | val code = CodeUtils.createTrainTicketCode( |
| | | msgId = msgId, |
| | | createTime = createtime, |
| | | company = response.data.details.company, |
| | | seat = response.data.details.seat, |
| | | time = response.data.details.time, |
| | | address = response.data.details.address, |
| | | trips = response.data.details.trips |
| | | ) |
| | | CodeUtils.saveCode(code) |
| | | } |
| | | |
| | | else -> {} |
| | | } |
| | | } |
| | | |
| | | // 发送广播通知数据已更新 |
| | | val updateIntent = Intent("com.example.firstapp.DATA_UPDATED") |
| | | context.sendBroadcast(updateIntent) |
| | |
| | | object CodeUtils { |
| | | private const val TAG = "CodeUtils" |
| | | |
| | | private val lock = Any() |
| | | |
| | | /** |
| | | * 保存code到数据库,如果已存在则跳过 |
| | | * @param code 要保存的code对象 |
| | | * @return Boolean 是否成功保存 |
| | | */ |
| | | fun saveCode(code: Code): Boolean { |
| | | // 检查必要字段是否为空 |
| | | if (code.oneLevel.isEmpty() || code.code.isEmpty()) { |
| | | Log.d(TAG, "保存失败:必要字段为空") |
| | | return false |
| | | } |
| | | synchronized(lock) { |
| | | // 检查必要字段是否为空 |
| | | if (code.oneLevel.isEmpty() || code.code.isEmpty()) { |
| | | Log.d(TAG, "保存失败:必要字段为空") |
| | | return false |
| | | } |
| | | |
| | | // 检查是否已存在相同记录 |
| | | val existingCode = Core.code.queryByTypeAndCodeAndDate( |
| | | code.category, |
| | | code.code, |
| | | code.smsTimestamp |
| | | ) |
| | | // 检查是否已存在相同记录 |
| | | val existingCode = Core.code.queryByTypeAndCodeAndDate( |
| | | code.category, |
| | | code.code, |
| | | code.createTime |
| | | ) |
| | | |
| | | return if (existingCode == null) { |
| | | try { |
| | | Core.code.insert(code) |
| | | Log.d(TAG, "成功保存${code.category}记录: ${code.code}") |
| | | true |
| | | } catch (e: Exception) { |
| | | Log.e(TAG, "保存${code.category}记录失败: ${e.message}") |
| | | return if (existingCode == null) { |
| | | try { |
| | | Core.code.insert(code) |
| | | Log.d(TAG, "成功保存${code.category}记录: ${code.code}") |
| | | true |
| | | } catch (e: Exception) { |
| | | Log.e(TAG, "保存${code.category}记录失败: ${e.message}") |
| | | false |
| | | } |
| | | } else { |
| | | Log.d(TAG, "发现重复${code.category}记录,跳过保存: ${code.code}") |
| | | false |
| | | } |
| | | } else { |
| | | Log.d(TAG, "发现重复${code.category}记录,跳过保存: ${code.code}") |
| | | false |
| | | } |
| | | } |
| | | |
| | |
| | | company: String?, |
| | | pickupCode: String?, |
| | | address: String?, |
| | | time: String?, |
| | | smsTimestamp: Long = 0 |
| | | time: String? |
| | | ): Code { |
| | | // val secondLevel = if (company.isNullOrEmpty()) "未知" else company |
| | | return Code( |
| | |
| | | pickupTime = "", |
| | | overTime = "", |
| | | address = address ?: "", |
| | | remarks = time ?: "", |
| | | smsTimestamp = smsTimestamp |
| | | remarks = time ?: "" |
| | | ) |
| | | } |
| | | |
| | |
| | | date: String?, |
| | | address: String?, |
| | | minAmount: String?, |
| | | number: String?, |
| | | smsTimestamp: Long = 0 |
| | | number: String? |
| | | ): Code { |
| | | return Code( |
| | | id = 0, |
| | |
| | | pickupTime = "", |
| | | overTime = date ?: "", |
| | | address = address ?: "", |
| | | remarks = "最小还款金额${minAmount ?: ""}还款卡号${number ?: ""}", |
| | | smsTimestamp = smsTimestamp |
| | | remarks = "最小还款金额${minAmount ?: ""}还款卡号${number ?: ""}" |
| | | ) |
| | | } |
| | | |
| | |
| | | amount: String?, |
| | | datetime: String?, |
| | | address: String?, |
| | | balance: String?, |
| | | smsTimestamp: Long = 0 |
| | | balance: String? |
| | | ): Code { |
| | | return Code( |
| | | id = 0, |
| | |
| | | pickupTime = "", |
| | | overTime = datetime ?: "", |
| | | address = address ?: "", |
| | | remarks = "余额${balance ?: ""}", |
| | | smsTimestamp = smsTimestamp |
| | | remarks = "余额${balance ?: ""}" |
| | | ) |
| | | } |
| | | |
| | |
| | | end: String?, |
| | | seat: String?, |
| | | time: String?, |
| | | address: String?, |
| | | smsTimestamp: Long = 0 |
| | | address: String? |
| | | ): Code { |
| | | return Code( |
| | | id = 0, |
| | |
| | | pickupTime = "", |
| | | overTime = time ?: "", |
| | | address = address ?: "", |
| | | remarks = seat ?: "", |
| | | smsTimestamp = smsTimestamp |
| | | remarks = seat ?: "" |
| | | ) |
| | | } |
| | | |
| | |
| | | seat: String?, |
| | | time: String?, |
| | | address: String?, |
| | | trips: String?, |
| | | smsTimestamp: Long = 0 |
| | | trips: String? |
| | | ): Code { |
| | | return Code( |
| | | id = 0, |
| | |
| | | pickupTime = "", |
| | | overTime = time ?: "", |
| | | address = address ?: "", |
| | | remarks = trips ?: "", |
| | | smsTimestamp = smsTimestamp |
| | | remarks = trips ?: "" |
| | | ) |
| | | } |
| | | } |
| | | } |