tj
10 天以前 d166fa86711d9348ac987ba87715e986a3b1a27b
app/src/main/java/com/example/firstapp/MainActivity.kt
@@ -5,127 +5,559 @@
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 androidx.lifecycle.ViewModelProvider
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.example.firstapp.adapter.MyAdapter
import android.content.pm.PackageManager
import android.net.Uri
import android.os.Build
import androidx.annotation.RequiresApi
import com.example.firstapp.activity.LoginActivity
import com.example.firstapp.core.Core
import com.example.firstapp.entity.Item
import com.example.firstapp.ui.home.HomeViewModel
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.utils.Log
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import java.text.SimpleDateFormat
import java.util.Calendar
import java.util.Date
import java.util.Locale
import android.app.AlertDialog
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 smsPermissionRequest =
        registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) { permissions ->
            when {
                permissions.getOrDefault(
                    Manifest.permission.RECEIVE_SMS, false
                ) && permissions.getOrDefault(Manifest.permission.READ_SMS, false) -> {
                    // 两个权限都获得授权
                    Toast.makeText(this, "短信权限已授予", Toast.LENGTH_SHORT).show()
                    registerSmsReceiver()
                    // 在Android O及以上版本同步最近短信
                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                        syncRecentSms()
                    }
                }
    // 权限请求代码
    private val permissionRequest = registerForActivityResult(ActivityResultContracts.RequestPermission()) { isGranted ->
        if (isGranted) {
            // 权限授予后注册短信监听器
            registerSmsReceiver()
        } else {
            // 权限拒绝,提示用户
            Toast.makeText(this, "Permission Denied", Toast.LENGTH_SHORT).show()
                else -> {
                    // 有权限被拒绝
                    showSmsPermissionExplanationDialog()
                }
            }
        }
    }
    // 通知权限请求
    private val notificationPermissionRequest =
        registerForActivityResult(ActivityResultContracts.RequestPermission()) { isGranted ->
            if (isGranted) {
                // 权限已授予
                Toast.makeText(this, "通知权限已授予,您将能收到重要提醒", Toast.LENGTH_SHORT).show()
            } else {
                // 权限被拒绝
                Toast.makeText(this, "通知权限被拒绝,应用将无法发送提醒通知", Toast.LENGTH_SHORT).show()
            }
            // 无论通知权限是否授予,都继续请求短信权限
            requestSmsPermissions()
        }
    private val syncLock = Object()
    @RequiresApi(Build.VERSION_CODES.O)
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)
        // 在此位置初始化 homeViewModel
        homeViewModel = ViewModelProvider(this).get(HomeViewModel::class.java)
        val navView: BottomNavigationView = binding.navView
        setupViews()
        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)
        } else {
            // 权限已经授予,继续执行相关操作
            registerSmsReceiver()
        }
        // 重置提醒计划并检查是否有错过的提醒
        resetReminders()
        // 开始权限请求流程
        startPermissionsFlow()
    }
        // 模拟数据
//        val items = listOf(
//            Item(1, "First Item", "This is the first item description"),
//            Item(2, "Second Item", "This is the second item description"),
//            Item(3, "Third Item", "This is the third item description")
//        )
//
//        val recyclerView = findViewById<RecyclerView>(R.id.recyclerView)
//        recyclerView.layoutManager = LinearLayoutManager(this)
//        recyclerView.adapter = MyAdapter(items)
//        var codeList = Core.code.getAllDesc()
        val recyclerView = findViewById<RecyclerView>(R.id.recyclerView)
        recyclerView.layoutManager = LinearLayoutManager(this)
//        recyclerView.adapter = MyAdapter(codeList)
        // 初始化适配器
        adapter = MyAdapter()
        recyclerView.adapter = adapter
        // 观察 LiveData 数据
        homeViewModel.codeList.observe(this) { codeList ->
            // 如果 codeList 为 null,避免闪退
            if (codeList != null) {
                adapter.submitList(codeList)
                // 滚动到顶部
                recyclerView.scrollToPosition(0)
    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,
                    Manifest.permission.POST_NOTIFICATIONS
                ) != PackageManager.PERMISSION_GRANTED
            ) {
                notificationPermissionRequest.launch(Manifest.permission.POST_NOTIFICATIONS)
            } else {
                // 如果数据为空,可以显示空列表或其他处理
                Toast.makeText(this, "No data available", Toast.LENGTH_SHORT).show()
                // 已有通知权限,直接请求短信权限
                requestSmsPermissions()
            }
        } else {
            // 低版本Android无需请求通知权限,直接请求短信权限
            requestSmsPermissions()
        }
    }
    // 请求短信权限
    private fun requestSmsPermissions() {
        if (!hasSmsPermissions()) {
            // 没有短信权限,请求权限
            smsPermissionRequest.launch(
                arrayOf(
                    Manifest.permission.RECEIVE_SMS, Manifest.permission.READ_SMS
                )
            )
        } else {
            // 已有短信权限,直接初始化短信处理
            registerSmsReceiver()
            // 在Android O及以上版本同步最近短信
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                syncRecentSms()
            }
        }
        // 注册广播接收器来监听数据更新
        val filter = IntentFilter("com.example.firstapp.DATA_UPDATED")
        registerReceiver(object : BroadcastReceiver() {
            override fun onReceive(context: Context, intent: Intent) {
                // 数据已更新,刷新 LiveData
                homeViewModel.loadData()
    }
    // 检查并处理短信权限(可选是否显示对话框)
    private fun checkAndHandleSmsPermissions(showDialog: Boolean = true) {
        if (!hasSmsPermissions()) {
            if (showDialog) {
                showSmsPermissionExplanationDialog()
            }
        }, filter)
        } else {
            // 已有短信权限,确保接收器已注册
            if (smsReceiver == null) {
                registerSmsReceiver()
            }
        }
    }
    // 检查是否有短信权限
    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
    }
    // 显示短信权限解释对话框
    private fun showSmsPermissionExplanationDialog() {
        AlertDialog.Builder(this)
            .setTitle("需要短信权限")
            .setMessage("应用需要短信权限来自动处理和分类您的短信信息,包括快递、付款等内容。没有此权限,应用核心功能将无法正常工作。")
            .setPositiveButton("授予权限") { _, _ ->
                requestSmsPermissions()
            }
            .setNegativeButton("暂时不要") { dialog, _ ->
                dialog.dismiss()
                Toast.makeText(this, "您可以稍后在设置中开启短信权限", Toast.LENGTH_LONG).show()
            }
            .setCancelable(false)
            .show()
    }
    // 提供给外部调用的请求通知权限方法
    fun requestNotificationPermission() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
            // 判断是否已经有通知权限
            if (ContextCompat.checkSelfPermission(
                    this,
                    Manifest.permission.POST_NOTIFICATIONS
                ) != PackageManager.PERMISSION_GRANTED
            ) {
                notificationPermissionRequest.launch(Manifest.permission.POST_NOTIFICATIONS)
            } else {
                Toast.makeText(this, "已经拥有通知权限", Toast.LENGTH_SHORT).show()
            }
        } else {
            // Android 13以下的版本不需要请求权限
            Toast.makeText(this, "当前系统版本无需单独请求通知权限", Toast.LENGTH_SHORT).show()
        }
    }
    private fun registerSmsReceiver() {
        // 确保不重复注册
        if (smsReceiver != null) return
        Log.d("SMS_DEBUG", "MainActivity注册短信接收器")
        smsReceiver = SmsReceiver()
        val filter = IntentFilter(Telephony.Sms.Intents.SMS_RECEIVED_ACTION)
        registerReceiver(smsReceiver, filter)
    }
    private fun setupViews() {
        // 获取并显示当前登录的手机号
        getSharedPreferences("user_info", Context.MODE_PRIVATE).getString("phone", "") ?: ""
    }
    private fun logout() {
        // 清除登录信息
        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)
            val threeDaysAgo = calendar.timeInMillis
            val cursor = contentResolver.query(
                Uri.parse("content://sms/inbox"),
                arrayOf("address", "body", "date"),
                "date >= ?",
                arrayOf(threeDaysAgo.toString()),
                "date DESC"
            )
            cursor?.use {
                while (cursor.moveToNext()) {
                    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 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 {
                            val response =
                                RetrofitModelClient.modelService.processSms(mapOf("content" to messageBody))
                            synchronized(syncLock) {
                                if (response.status == "success") {
                                    when (response.data.category) {
                                        "快递" -> {
                                            val pickupCode = response.data.details.pickupCode ?: ""
                                            if (pickupCode.isNotEmpty()) {
                                                val existingCode =
                                                    Core.code.queryByTypeAndCodeAndDate(
                                                        response.data.category,
                                                        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 = pickupCode,
                                                        pickup = 0,
                                                        pickupTime = "",
                                                        overTime = "",
                                                        address = response.data.details.address
                                                            ?: "",
                                                        remarks = response.data.details.time ?: "",
                                                    )
//                                                    if(code.oneLevel.isNotEmpty() && code.secondLevel.isNotEmpty() && code.code.isNotEmpty()) {
                                                    if (code.oneLevel.isNotEmpty() && code.code.isNotEmpty()) {
                                                        Core.code.insert(code)
                                                        android.util.Log.d(
                                                            "MainActivity",
                                                            "历史快递短信已保存: $pickupCode"
                                                        )
                                                    }
                                                } else {
                                                    android.util.Log.d(
                                                        "MainActivity",
                                                        "发现重复快递短信,跳过保存: $pickupCode"
                                                    )
                                                }
                                            }
                                        }
                                        "还款" -> {
                                            val amount = response.data.details.amount ?: ""
                                            if (amount.isNotEmpty()) {
                                                val existingCode =
                                                    Core.code.queryByTypeAndCodeAndDate(
                                                        response.data.category, 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 = 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.isNotEmpty() && code.secondLevel.isNotEmpty() && code.code.isNotEmpty()) {
                                                        Core.code.insert(code)
                                                        android.util.Log.d(
                                                            "MainActivity",
                                                            "历史还款短信已保存: $amount"
                                                        )
                                                    }
                                                } else {
                                                    android.util.Log.d(
                                                        "MainActivity",
                                                        "发现重复还款短信,跳过保存: $amount"
                                                    )
                                                }
                                            }
                                        }
                                        "收入" -> {
                                            val amount = response.data.details.amount ?: ""
                                            if (amount.isNotEmpty()) {
                                                val existingCode =
                                                    Core.code.queryByTypeAndCodeAndDate(
                                                        response.data.category, 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 = amount,
                                                        pickup = 0, // 0-未取件,1-已取件
                                                        pickupTime = "", // 取件时间为空
                                                        overTime = response.data.details.datetime
                                                            ?: "",  // 超时时间为空,暂时没有这块处理逻辑
                                                        address = response.data.details.address
                                                            ?: "",
                                                        remarks = "余额" + response.data.details.balance
                                                            ?: "",
                                                    )
                                                    if (code.oneLevel.isNotEmpty() && code.secondLevel.isNotEmpty() && code.code.isNotEmpty()) {
                                                        Core.code.insert(code)
                                                        android.util.Log.d(
                                                            "MainActivity",
                                                            "历史还款短信已保存: $amount"
                                                        )
                                                    }
                                                } else {
                                                    android.util.Log.d(
                                                        "MainActivity",
                                                        "发现重复还款短信,跳过保存: $amount"
                                                    )
                                                }
                                            }
                                        }
                                        "航班" -> {
                                            val flight = response.data.details.flight ?: ""
                                            if (flight.isNotEmpty()) {
                                                val existingCode =
                                                    Core.code.queryByTypeAndCodeAndDate(
                                                        response.data.category, flight, dateString
                                                    )
                                                if (existingCode == null) {
                                                    val code = Code(
                                                        id = 0,
                                                        category = response.data.category,
                                                        categoryId = 4, // 4-航班类型
                                                        typeId = 1,     //暂时没有根据type分类
                                                        ruleId = 2,     //1-还款类型
                                                        msgId = msgId,
                                                        createTime = dateString,
                                                        oneLevel = response.data.details.company
                                                            ?: "未知",
                                                        secondLevel = response.data.details.start + response.data.details.end
                                                            ?: "",
                                                        code = flight,
                                                        pickup = 0, // 0-未取件,1-已取件
                                                        pickupTime = "", // 取件时间为空
                                                        overTime = response.data.details.time
                                                            ?: "",  // 超时时间为空,暂时没有这块处理逻辑
                                                        address = response.data.details.address
                                                            ?: "",
                                                        remarks = response.data.details.seat ?: "",
                                                    )
                                                    if (code.oneLevel != "" && code.secondLevel != "" && code.code != "") {
                                                        Core.code.insert(code)
                                                    }
                                                } else {
                                                    android.util.Log.d(
                                                        "MainActivity",
                                                        "发现重复还款短信,跳过保存: $flight"
                                                    )
                                                }
                                            }
                                        }
                                        "火车票" -> {
                                            val seat = response.data.details.seat ?: ""
                                            if (seat.isNotEmpty()) {
                                                val existingCode =
                                                    Core.code.queryByTypeAndCodeAndDate(
                                                        response.data.category, seat, dateString
                                                    )
                                                if (existingCode == null) {
                                                    val code = Code(
                                                        id = 0,
                                                        category = response.data.category,
                                                        categoryId = 5, // 5-火车票类型
                                                        typeId = 1,     //暂时没有根据type分类
                                                        ruleId = 2,     //1-还款类型
                                                        msgId = msgId,
                                                        createTime = dateString,
                                                        oneLevel = response.data.details.company
                                                            ?: "",
                                                        secondLevel = response.data.details.company
                                                            ?: "未知",
                                                        code = seat,
                                                        pickup = 0, // 0-未取件,1-已取件
                                                        pickupTime = "", // 取件时间为空
                                                        overTime = response.data.details.time
                                                            ?: "",  // 超时时间为空,暂时没有这块处理逻辑
                                                        address = response.data.details.address
                                                            ?: "",
                                                        remarks = response.data.details.trips ?: "",
                                                    )
                                                    if (code.oneLevel != "" && code.secondLevel != "" && code.code != "") {
                                                        Core.code.insert(code)
                                                    }
                                                } else {
                                                    android.util.Log.d(
                                                        "MainActivity",
                                                        "发现重复还款短信,跳过保存: $seat"
                                                    )
                                                }
                                            }
                                        }
                                    }
                                    // 发送广播通知数据已更新
                                    val updateIntent = Intent("com.example.firstapp.DATA_UPDATED")
                                    sendBroadcast(updateIntent)
                                }
                            }
                        } catch (e: Exception) {
                            android.util.Log.e("MainActivity", "处理历史短信出错: ${e.message}", e)
                        }
                    }
                }
            }
        } catch (e: Exception) {
            e.printStackTrace()
            Toast.makeText(this, "同步短信失败:${e.message}", Toast.LENGTH_SHORT).show()
        }
    }
    // 重置提醒计划
    private fun resetReminders() {
        try {
            // 取消所有现有的提醒任务
            androidx.work.WorkManager.getInstance(this).cancelAllWorkByTag("reminder_worker")
            // 清除旧的提醒偏好设置
            getSharedPreferences("reminder_prefs", Context.MODE_PRIVATE).edit().clear().apply()
            // 重新设置提醒任务
            (application as App).setupReminderWorker()
            // 检查是否有错过的提醒
            com.example.firstapp.service.ReminderWorker.checkMissedReminders(this)
            android.util.Log.d("MainActivity", "已重置提醒计划")
        } catch (e: Exception) {
            android.util.Log.e("MainActivity", "重置提醒计划失败: ${e.message}")
        }
    }
}