| | |
| | | import android.provider.Telephony |
| | | import android.widget.Toast |
| | | import androidx.activity.result.contract.ActivityResultContracts |
| | | import com.google.android.material.bottomnavigation.BottomNavigationView |
| | | import androidx.appcompat.app.AppCompatActivity |
| | | import androidx.core.content.ContextCompat |
| | | import androidx.navigation.findNavController |
| | | import androidx.navigation.ui.AppBarConfiguration |
| | | import androidx.navigation.ui.setupActionBarWithNavController |
| | | import androidx.navigation.ui.setupWithNavController |
| | | import com.example.firstapp.databinding.ActivityMainBinding |
| | | import com.example.firstapp.receiver.SmsReceiver |
| | | import android.Manifest |
| | | import android.content.BroadcastReceiver |
| | | import android.content.Context |
| | | import android.content.Intent |
| | | import android.net.Uri |
| | | import androidx.lifecycle.ViewModelProvider |
| | | import androidx.recyclerview.widget.LinearLayoutManager |
| | | import androidx.recyclerview.widget.RecyclerView |
| | | import android.os.Build |
| | | import androidx.annotation.RequiresApi |
| | | import androidx.work.ExistingPeriodicWorkPolicy |
| | | import androidx.work.PeriodicWorkRequestBuilder |
| | | import androidx.work.WorkManager |
| | | import com.example.firstapp.activity.LoginActivity |
| | | import com.example.firstapp.adapter.MyAdapter |
| | | import com.example.firstapp.core.Core |
| | | import com.example.firstapp.database.entity.Code |
| | | import com.example.firstapp.database.entity.Msg |
| | | import com.example.firstapp.database.service.RetrofitClient |
| | | import com.example.firstapp.database.service.RetrofitModelClient |
| | | import com.example.firstapp.ui.home.HomeViewModel |
| | | import com.example.firstapp.utils.Log |
| | | import com.example.firstapp.workers.KeywordUpdateWorker |
| | | import kotlinx.coroutines.CoroutineScope |
| | | import kotlinx.coroutines.Dispatchers |
| | | import kotlinx.coroutines.launch |
| | | import java.text.SimpleDateFormat |
| | | import java.time.LocalDateTime |
| | | import java.util.Calendar |
| | | import java.util.Date |
| | | import java.util.Locale |
| | | import java.util.concurrent.TimeUnit |
| | | import java.time.ZoneId |
| | | |
| | | class MainActivity : AppCompatActivity() { |
| | | // 安全防护关键词数组 |
| | | private var securityKeywordsList = emptyList<String>() |
| | | |
| | | private lateinit var binding: ActivityMainBinding |
| | | |
| | | private var smsReceiver:SmsReceiver? = null |
| | | private var smsReceiver: SmsReceiver? = null |
| | | |
| | | private lateinit var adapter: MyAdapter |
| | | private lateinit var homeViewModel: HomeViewModel |
| | | |
| | | private val multiplePermissionRequest = |
| | | registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) { permissions -> |
| | | when { |
| | | permissions.getOrDefault( |
| | | Manifest.permission.RECEIVE_SMS, |
| | | false |
| | | ) && permissions.getOrDefault(Manifest.permission.READ_SMS, false) -> { |
| | | // 两个权限都获得授权 |
| | | registerSmsReceiver() |
| | | // syncRecentSms() |
| | | // initializeSecurityKeywords() |
| | | } |
| | | |
| | | // 权限请求代码 |
| | | private val permissionRequest = registerForActivityResult(ActivityResultContracts.RequestPermission()) { isGranted -> |
| | | if (isGranted) { |
| | | // 权限授予后注册短信监听器 |
| | | registerSmsReceiver() |
| | | syncRecentSms() |
| | | } else { |
| | | // 权限拒绝,提示用户 |
| | | Toast.makeText(this, "Permission Denied", Toast.LENGTH_SHORT).show() |
| | | else -> { |
| | | // 有权限被拒绝 |
| | | Toast.makeText( |
| | | this, "需要短信读取和接收权限才能正常使用功能", Toast.LENGTH_SHORT |
| | | ).show() |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | private val syncLock = Object() |
| | | |
| | | @RequiresApi(Build.VERSION_CODES.O) |
| | | override fun onCreate(savedInstanceState: Bundle?) { |
| | | super.onCreate(savedInstanceState) |
| | | |
| | |
| | | // val navView: BottomNavigationView = binding.navView |
| | | val navView = binding.navView |
| | | val navController = findNavController(R.id.nav_host_fragment_activity_main) |
| | | // Passing each menu ID as a set of Ids because each |
| | | // menu should be considered as top level destinations. |
| | | val appBarConfiguration = AppBarConfiguration( |
| | | setOf( |
| | | R.id.navigation_home, R.id.navigation_dashboard, R.id.navigation_notifications |
| | | ) |
| | | ) |
| | | setupActionBarWithNavController(navController, appBarConfiguration) |
| | | |
| | | // 只保留底部导航的设置 |
| | | navView.setupWithNavController(navController) |
| | | |
| | | // 检查权限 |
| | | if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_SMS) != android.content.pm.PackageManager.PERMISSION_GRANTED) { |
| | | // 请求权限 |
| | | permissionRequest.launch(Manifest.permission.READ_SMS) |
| | | syncRecentSms() |
| | | if (ContextCompat.checkSelfPermission( |
| | | this, Manifest.permission.RECEIVE_SMS |
| | | ) != android.content.pm.PackageManager.PERMISSION_GRANTED || ContextCompat.checkSelfPermission( |
| | | this, Manifest.permission.READ_SMS |
| | | ) != android.content.pm.PackageManager.PERMISSION_GRANTED |
| | | ) { |
| | | // 同时请求两个权限 |
| | | multiplePermissionRequest.launch( |
| | | arrayOf( |
| | | Manifest.permission.RECEIVE_SMS, Manifest.permission.READ_SMS |
| | | ) |
| | | ) |
| | | } else { |
| | | // 权限已经授予,继续执行相关操作 |
| | | registerSmsReceiver() |
| | |
| | | |
| | | private fun logout() { |
| | | // 清除登录信息 |
| | | getSharedPreferences("user_info", Context.MODE_PRIVATE) |
| | | .edit() |
| | | .clear() |
| | | .apply() |
| | | getSharedPreferences("user_info", Context.MODE_PRIVATE).edit().clear().apply() |
| | | |
| | | // 跳转回登录页 |
| | | startActivity(Intent(this, LoginActivity::class.java)) |
| | | finish() |
| | | } |
| | | |
| | | // 初始化禁用词 |
| | | @RequiresApi(Build.VERSION_CODES.O) |
| | | private fun initializeSecurityKeywords() { |
| | | CoroutineScope(Dispatchers.IO).launch { |
| | | try { |
| | | val response = RetrofitClient.apiService.getSecurityList() |
| | | if (response.code == 200) { |
| | | securityKeywordsList = response.data.map { it.keyword } |
| | | android.util.Log.d( |
| | | "MainActivity", "securityKeywordsList: $securityKeywordsList" |
| | | ) |
| | | // 确保在主线程中调用 syncRecentSms |
| | | runOnUiThread { |
| | | syncRecentSms() |
| | | } |
| | | } else { |
| | | android.util.Log.e( |
| | | "MainActivity", "Failed to get security list: ${response.code}" |
| | | ) |
| | | } |
| | | } catch (e: Exception) { |
| | | android.util.Log.e("MainActivity", "Error fetching security list", e) |
| | | } |
| | | } |
| | | } |
| | | |
| | | @RequiresApi(Build.VERSION_CODES.O) |
| | | private fun syncRecentSms() { |
| | | try { |
| | | val calendar = Calendar.getInstance() |
| | | calendar.add(Calendar.DAY_OF_YEAR, -3) // 获取3天前的时间 |
| | | calendar.add(Calendar.DAY_OF_YEAR, -3) |
| | | val threeDaysAgo = calendar.timeInMillis |
| | | |
| | | val cursor = contentResolver.query( |
| | | Uri.parse("content://sms/sent"), |
| | | Uri.parse("content://sms/inbox"), |
| | | arrayOf("address", "body", "date"), |
| | | "date >= ?", |
| | | arrayOf(threeDaysAgo.toString()), |
| | |
| | | |
| | | cursor?.use { |
| | | while (cursor.moveToNext()) { |
| | | val address = cursor.getString(cursor.getColumnIndexOrThrow("address")) |
| | | val body = cursor.getString(cursor.getColumnIndexOrThrow("body")) |
| | | val date = cursor.getLong(cursor.getColumnIndexOrThrow("date")) |
| | | val messageBody = cursor.getString(cursor.getColumnIndexOrThrow("body")) |
| | | val datetime = cursor.getLong(cursor.getColumnIndexOrThrow("date")) |
| | | |
| | | // 转换为 Date 对象 |
| | | val date = Date(datetime) |
| | | val sdf = SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault()) |
| | | val dateString = sdf.format(date) |
| | | |
| | | // 使用正则表达式提取验证码 |
| | | val regex = "\\d{4,6}".toRegex() |
| | | val matchResult = regex.find(body) |
| | | matchResult?.let { result -> |
| | | val code = result.value |
| | | // 使用 ViewModel 保存到数据库 |
| | | println("address") |
| | | println(address) |
| | | println(code) |
| | | println(date) |
| | | // homeViewModel.saveCode(address, code, date) |
| | | // 保存原始短信 |
| | | val msg = Msg(0, "1111", "111111", messageBody, 1, "111", 1, 1) |
| | | val msgId = Core.msg.insert(msg) |
| | | |
| | | // 禁用关键词拦截 |
| | | if (securityKeywordsList.any { it in messageBody }) { |
| | | android.util.Log.d("MainActivity", "历史短信含有禁用关键词,跳过处理") |
| | | continue |
| | | } |
| | | |
| | | // 使用协程处理API调用和数据库操作 |
| | | CoroutineScope(Dispatchers.IO).launch { |
| | | try { |
| | | // API调用移到synchronized块外 |
| | | val response = RetrofitModelClient.modelService.processSms(mapOf("content" to messageBody)) |
| | | |
| | | // 数据库操作放在synchronized块内 |
| | | synchronized(syncLock) { |
| | | if (response.status == "success") { |
| | | when (response.data.category) { |
| | | "快递" -> { |
| | | val existingCode = Core.code.queryByTypeAndCodeAndDate( |
| | | response.data.category, |
| | | response.data.details.pickupCode ?: "", |
| | | dateString |
| | | ) |
| | | |
| | | if (existingCode == null) { |
| | | val code = Code( |
| | | id = 0, |
| | | category = response.data.category, |
| | | categoryId = 1, |
| | | typeId = 1, |
| | | ruleId = 1, |
| | | msgId = msgId, |
| | | createTime = dateString, |
| | | oneLevel = response.data.details.post ?: "", |
| | | secondLevel = response.data.details.company ?: "", |
| | | code = response.data.details.pickupCode ?: "", |
| | | pickup = 0, |
| | | pickupTime = "", |
| | | overTime = "", |
| | | address = response.data.details.address ?: "", |
| | | remarks = response.data.details.time ?: "", |
| | | ) |
| | | if(code.oneLevel!="" && code.secondLevel!="" && code.code!="") { |
| | | Core.code.insert(code) |
| | | } |
| | | android.util.Log.d("MainActivity", "历史快递短信已保存: ${response.data.details.pickupCode}") |
| | | } else { |
| | | android.util.Log.d("MainActivity", "发现重复快递短信,跳过保存: ${response.data.details.pickupCode}") |
| | | } |
| | | } |
| | | "还款" -> { |
| | | val existingCode = Core.code.queryByTypeAndCodeAndDate( |
| | | response.data.category, |
| | | response.data.details.amount ?: "", |
| | | dateString |
| | | ) |
| | | |
| | | if (existingCode == null) { |
| | | val code = Code( |
| | | id = 0, |
| | | category = response.data.category, |
| | | categoryId = 2, |
| | | typeId = 1, |
| | | ruleId = 2, |
| | | msgId = msgId, |
| | | createTime = dateString, |
| | | oneLevel = response.data.details.type ?: "", |
| | | secondLevel = response.data.details.bank ?: "", |
| | | code = response.data.details.amount ?: "", |
| | | pickup = 0, |
| | | pickupTime = "", |
| | | overTime = response.data.details.date ?: "", |
| | | address = response.data.details.address ?: "", |
| | | remarks = "最小还款金额${response.data.details.min_amount}还款卡号${response.data.details.number}" |
| | | ) |
| | | if(code.oneLevel!="" && code.secondLevel!="" && code.code!="") { |
| | | Core.code.insert(code) |
| | | } |
| | | android.util.Log.d("MainActivity", "历史还款短信已保存: ${response.data.details.amount}") |
| | | } else { |
| | | android.util.Log.d("MainActivity", "发现重复还款短信,跳过保存: ${response.data.details.amount}") |
| | | } |
| | | } |
| | | |
| | | "收入" -> { |
| | | val existingCode = Core.code.queryByTypeAndCodeAndDate( |
| | | response.data.category, |
| | | response.data.details.amount ?: "", |
| | | dateString |
| | | ) |
| | | |
| | | if (existingCode == null) { |
| | | val code = Code( |
| | | id = 0, |
| | | category = response.data.category, |
| | | categoryId = 3, // 3-收入类型 |
| | | typeId = 1, //暂时没有根据type分类 |
| | | ruleId = 2, //1-还款类型 |
| | | msgId = msgId, |
| | | createTime = dateString, |
| | | oneLevel = response.data.details.bank ?: "", |
| | | secondLevel = response.data.details.bank ?: "", |
| | | code = response.data.details.amount ?: "", |
| | | pickup = 0, // 0-未取件,1-已取件 |
| | | pickupTime = "", // 取件时间为空 |
| | | overTime = response.data.details.datetime |
| | | ?: "", // 超时时间为空,暂时没有这块处理逻辑 |
| | | address = response.data.details.address ?: "", |
| | | remarks = "余额" + response.data.details.balance ?: "", |
| | | ) |
| | | if(code.oneLevel!="" && code.secondLevel!="" && code.code!="") { |
| | | Core.code.insert(code) |
| | | } |
| | | android.util.Log.d("MainActivity", "历史还款短信已保存: ${response.data.details.amount}") |
| | | } else { |
| | | android.util.Log.d("MainActivity", "发现重复还款短信,跳过保存: ${response.data.details.amount}") |
| | | } |
| | | } |
| | | } |
| | | |
| | | // 发送广播通知数据已更新 |
| | | val updateIntent = Intent("com.example.firstapp.DATA_UPDATED") |
| | | sendBroadcast(updateIntent) |
| | | } |
| | | } |
| | | } catch (e: Exception) { |
| | | android.util.Log.e("MainActivity", "处理历史短信出错: ${e.message}", e) |
| | | } |
| | | } |
| | | } |
| | | } |