app/src/main/java/com/example/firstapp/adapter/FinanceAdapter.kt
对比新文件 @@ -0,0 +1,175 @@ package com.example.firstapp.adapter import android.view.LayoutInflater import android.view.ViewGroup import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.ListAdapter import androidx.recyclerview.widget.RecyclerView import com.example.firstapp.R import com.example.firstapp.databinding.ItemFinanceBinding import com.example.firstapp.databinding.ItemFinanceGroupBinding import com.example.firstapp.databinding.ItemFinancePackageHomeBinding import com.example.firstapp.model.FinanceGroup import com.example.firstapp.model.FinancePackage class FinanceAdapter : ListAdapter<FinanceGroup, FinanceAdapter.ViewHolder>(FinanceGroupDiffCallback()) { private var onPackageClickListener: (FinanceGroup, FinancePackage) -> Unit = { _, _ -> } fun setOnPackageClickListener(listener: (FinanceGroup, FinancePackage) -> Unit) { onPackageClickListener = listener } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { val binding = ItemFinanceGroupBinding.inflate( LayoutInflater.from(parent.context), parent, false ) return ViewHolder(binding) } override fun onBindViewHolder(holder: ViewHolder, position: Int) { val group = getItem(position) holder.bind(group) holder.setOnPackageClickListener(onPackageClickListener) } inner class ViewHolder(private val binding: ItemFinanceGroupBinding) : RecyclerView.ViewHolder(binding.root) { private val packagesAdapter = FinancePackageHomeAdapter { pack -> currentGroup?.let { group -> onPackageClickListener(group, pack) } } private var currentGroup: FinanceGroup? = null init { binding.rvPackages.apply { layoutManager = LinearLayoutManager(context) adapter = packagesAdapter } } fun bind(group: FinanceGroup) { currentGroup = group binding.tvStationName.text = group.stationName binding.tvPackageCount.text = "共${group.packages.size}笔账单" packagesAdapter.submitList(group.packages) } fun setOnPackageClickListener(listener: (FinanceGroup, FinancePackage) -> Unit) { // 这个方法可以移除,因为我们在构造 ExpressPackageHomeAdapter 时已经处理了点击事件 // 或者保留这个方法但不做任何操作 } } } // 首页使用的包裹适配器 - 简化版本 class FinancePackageHomeAdapter(private val onPackageClick: (FinancePackage) -> Unit) : ListAdapter<FinancePackage, FinancePackageHomeAdapter.ViewHolder>(FinancePackageDiffCallback()) { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { val binding = ItemFinancePackageHomeBinding.inflate( LayoutInflater.from(parent.context), parent, false ) return ViewHolder(binding) } override fun onBindViewHolder(holder: ViewHolder, position: Int) { val pack = getItem(position) holder.bind(pack) } inner class ViewHolder(private val binding: ItemFinancePackageHomeBinding) : RecyclerView.ViewHolder(binding.root) { init { binding.root.setOnClickListener { val pack = getItem(adapterPosition) onPackageClick(pack) } } fun bind(pack: FinancePackage) { binding.tvCompany.text = pack.company binding.tvCreateTime.text = pack.createTime binding.tvTrackingNumber.text = pack.trackingNumber } } } // 取件页面使用的包裹适配器 class FinancePackageAdapter(private val onPackagePickup: (FinancePackage) -> Unit = { _ -> }) : ListAdapter<FinancePackage, FinancePackageAdapter.ViewHolder>(FinancePackageDiffCallback()) { private var onPackageClickListener: (FinancePackage) -> Unit = {} private var stationName: String = "" fun setStationInfo(station: String) { stationName = station } fun setOnPackageClickListener(listener: (FinancePackage) -> Unit) { onPackageClickListener = listener } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { val binding = ItemFinanceBinding.inflate( LayoutInflater.from(parent.context), parent, false ) return ViewHolder(binding) } override fun onBindViewHolder(holder: ViewHolder, position: Int) { val pack = getItem(position) holder.bind(pack) } inner class ViewHolder(private val binding: ItemFinanceBinding) : RecyclerView.ViewHolder(binding.root) { init { binding.ivPackageStatus.setOnClickListener { val pack = getItem(adapterPosition) onPackagePickup(pack) } binding.root.setOnClickListener(null) } fun bind(pack: FinancePackage) { binding.tvPackageId.text = pack.id.toString() binding.tvCompany.text = pack.company binding.tvCreateTime.text = pack.createTime binding.tvTrackingNumber.text = pack.trackingNumber binding.ivPackageStatus.setImageResource( // if (pack.isPickedUp) R.drawable.circle_checked // else R.drawable.circle R.drawable.circle ) } } } private class FinanceGroupDiffCallback : DiffUtil.ItemCallback<FinanceGroup>() { override fun areItemsTheSame(oldItem: FinanceGroup, newItem: FinanceGroup): Boolean { return oldItem.stationName == newItem.stationName } override fun areContentsTheSame(oldItem: FinanceGroup, newItem: FinanceGroup): Boolean { return oldItem == newItem } } private class FinancePackageDiffCallback : DiffUtil.ItemCallback<FinancePackage>() { override fun areItemsTheSame(oldItem: FinancePackage, newItem: FinancePackage): Boolean { return oldItem.trackingNumber == newItem.trackingNumber } override fun areContentsTheSame(oldItem: FinancePackage, newItem: FinancePackage): Boolean { return oldItem == newItem } } app/src/main/java/com/example/firstapp/entity/Rule.kt
@@ -1,17 +1,77 @@ package com.example.firstapp.entity data class Rule(var type: String,var content: String ,var reg: String) { import java.time.LocalDateTime // 在 Rule 类中定义提取方法 fun extractCodeFromMessage(message: String): String? { // 如果 message 包含 content if (message.contains(content)) { // 使用 reg 作为正则表达式进行匹配 val regex = reg.toRegex() val matchResult = regex.find(message) return matchResult?.value // 如果找到匹配的内容,则返回 } return null // 如果不匹配,返回 null class Rule( val type: String, val content: String, val pattern: String ) { // 添加伴生对象存储多个正则表达式模式 companion object { private val BANK_PATTERNS = mapOf( "招商银行" to listOf( // 第一种格式:账单¥xxx,还款日MM月dd日 "账单[¥¥](\\d+\\.?\\d*).*还款日(\\d{2})月(\\d{2})日", // 第二种格式:账单金额xxx,还款日MM月dd日 "账单金额[::](\\d+\\.?\\d*).*还款日[::](\\d{2})月(\\d{2})日", // 可以继续添加其他格式... ), "中国银行" to listOf( "账单金额[::](\\d+\\.?\\d*).*还款日[::](\\d{1,2})日", // 可以添加其他格式... ), // 其他银行的模式... ) } fun extractCodeFromMessage(message: String): String? { if (type == "财务") { // 获取该银行的所有正则表达式模式 val patterns = BANK_PATTERNS[content] ?: listOf(pattern) // 尝试每一个正则表达式 for (pat in patterns) { val regex = pat.toRegex() val matchResult = regex.find(message) matchResult?.let { if (it.groupValues.size > 1) { return it.groupValues[1].replace("[¥¥]".toRegex(), "") } } } return null } else { // 非财务类型的处理保持不变 val regex = pattern.toRegex() val matchResult = regex.find(message) return matchResult?.value } } fun extractDueDate(message: String): String? { if (type != "财务") return null val patterns = BANK_PATTERNS[content] ?: listOf(pattern) for (pat in patterns) { val regex = pat.toRegex() val matchResult = regex.find(message) matchResult?.let { when { content == "招商银行" && it.groupValues.size > 3 -> { return "${it.groupValues[2]}月${it.groupValues[3]}日" } it.groupValues.size > 2 -> { return it.groupValues[2] + "日" } else -> {} } } } return null } } app/src/main/java/com/example/firstapp/model/FinanceGroup.kt
对比新文件 @@ -0,0 +1,13 @@ package com.example.firstapp.model data class FinanceGroup( val stationName: String, val packages: List<FinancePackage> ) data class FinancePackage( var id: Long, val company: String, val trackingNumber: String, val createTime: String ) app/src/main/java/com/example/firstapp/receiver/SmsReceiver.kt
@@ -44,17 +44,19 @@ messages[i] = SmsMessage.createFromPdu(pdus[i] as ByteArray) messageBody.append(messages[i]?.messageBody) } // 输出短信内容到控制台 Log.d("SmsReceiver", "Received SMS: ${messageBody.toString()}") val msg = Msg(0, "1111", "111111", messageBody.toString(),1, "111", 1, 1) val msgId = Core.msg.insert(msg) Log.d("SmsReceiver", "Received SMS msgId: ${msgId}") // 这里我要写个数组,并创建个对象存放一些内容,如这个对象的属性有匹配内容,正则表达式,并循环遍历 val ruleList = mutableListOf( Rule("快递","京东","\\d{6}"), Rule("快递","菜鸟驿站","\\d{1,2}-\\d{1,2}-\\d{4}") Rule("快递","菜鸟驿站","\\d{1,2}-\\d{1,2}-\\d{4}"), // 银行规则使用默认正则,实际匹配时会使用 BANK_PATTERNS 中的模式 Rule("财务", "中国银行", "账单金额[::](\\d+\\.?\\d*).*还款日[::](\\d{1,2})日"), Rule("财务", "工商银行", "账单金额[::](\\d+\\.?\\d*).*还款日[::](\\d{1,2})日"), Rule("财务", "建设银行", "账单金额[::](\\d+\\.?\\d*).*还款日[::](\\d{1,2})日"), Rule("财务", "信用卡", "账单[¥¥](\\d+\\.?\\d*).*还款日(\\d{2})月(\\d{2})日"), Rule("财务", "花呗", "本月花呗账单(\\d+\\.?\\d*)元.*还款日[期是::](\\d{1,2})[日号]") ) CoroutineScope(Dispatchers.IO).launch { @@ -74,19 +76,49 @@ Log.d("RuleList", ruleList.toString()) for (rule in ruleList) { val code = rule.extractCodeFromMessage(messageBody.toString()) if (code!==null) { // 转换为 Date 对象 val code = if (rule.type == "财务") { // 对信用卡账单使用特殊的提取逻辑 val regex = rule.pattern.toRegex() val matchResult = regex.find(messageBody.toString()) matchResult?.let { val amount = it.groupValues[1] // 账单金额 amount } } else { rule.extractCodeFromMessage(messageBody.toString()) } if (code !== null) { val currentTime = LocalDateTime.now() // 将 LocalDateTime 转换为 Date val date = Date.from(currentTime.atZone(ZoneId.systemDefault()).toInstant()) // 如果需要格式化显示 val sdf = SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault()) val createtime = sdf.format(date) // 对于信用卡账单,使用还款日作为time字段 val time = if (rule.type == "财务") { rule.extractDueDate(messageBody.toString()) ?: createtime } else { createtime } val existingCode = Core.code.queryByTypeAndCodeAndDate(rule.content, code, createtime) if (existingCode == null) { val code = Code(0, rule.type, 1, rule.content, 1, 1, msgId, code, createtime, "中通",0,"","") Core.code.insert(code) val codeEntity = Code( 0, rule.type, 1, rule.content, 1, 1, msgId, code, createtime, rule.content, // 银行名称作为name 0, time, // 还款日期 "" ) Core.code.insert(codeEntity) Log.d("SMS_DEBUG", "新短信已保存到数据库") // 发送广播通知数据已更新 app/src/main/java/com/example/firstapp/ui/home/HomeFragment.kt
@@ -17,6 +17,7 @@ import com.example.firstapp.R import com.example.firstapp.activity.PickupActivity import com.example.firstapp.adapter.ExpressAdapter import com.example.firstapp.adapter.FinanceAdapter import com.example.firstapp.core.Core import com.example.firstapp.databinding.FragmentHomeBinding @@ -30,7 +31,7 @@ private lateinit var homeViewModel: HomeViewModel private lateinit var expressAdapter: ExpressAdapter // private lateinit var financeAdapter: FinanceAdapter private lateinit var financeAdapter: FinanceAdapter // private lateinit var memorialAdapter: MemorialAdapter private lateinit var dataUpdateReceiver: BroadcastReceiver @@ -88,12 +89,25 @@ } } // // 财务列表 // binding.financeRecycler.apply { // layoutManager = LinearLayoutManager(context) // financeAdapter = FinanceAdapter() // adapter = financeAdapter // } // 财务列表 binding.financeRecycler.apply { layoutManager = LinearLayoutManager(context) financeAdapter = FinanceAdapter() adapter = financeAdapter // 设置初始状态 - 添加这行 binding.financeContent.visibility = View.GONE // 设置点击监听 financeAdapter.setOnPackageClickListener { group, pack -> // 跳转到取件页面 val intent = Intent(requireContext(), PickupActivity::class.java).apply { putExtra("station_name", group.stationName) putExtra("company", pack.company) } startActivity(intent) } } // // // 纪念日列表 // binding.memorialRecycler.apply { @@ -132,6 +146,9 @@ tabExpress.textSize = 14f tabFinance.textSize = 16f others.textSize = 14f // 在切换到财务标签时加载数据 - 添加这行 homeViewModel.loadFinanceData() } // 其他标签点击事件 @@ -156,9 +173,9 @@ expressAdapter.submitList(items) } // homeViewModel.financeItems.observe(viewLifecycleOwner) { items -> // financeAdapter.submitList(items) // } homeViewModel.financeItems.observe(viewLifecycleOwner) { items -> financeAdapter.submitList(items) } // // homeViewModel.memorialItems.observe(viewLifecycleOwner) { items -> // memorialAdapter.submitList(items) app/src/main/java/com/example/firstapp/ui/home/HomeViewModel.kt
@@ -8,44 +8,29 @@ import com.example.firstapp.database.entity.Code import com.example.firstapp.model.ExpressGroup import com.example.firstapp.model.ExpressPackage import com.example.firstapp.model.FinanceGroup import com.example.firstapp.model.FinancePackage import kotlinx.coroutines.launch class HomeViewModel : ViewModel() { // private val _text = MutableLiveData<String>().apply { // value = "短信主页面" // } // val text: LiveData<String> = _text // // private val _codeList = MutableLiveData<List<Code>>() // // val codeList: LiveData<List<Code>> get() = _codeList private val _expressItems = MutableLiveData<List<ExpressGroup>>() private val _financeItems = MutableLiveData<List<FinanceGroup>>() val expressItems: LiveData<List<ExpressGroup>> = _expressItems val financeItems: LiveData<List<FinanceGroup>> = _financeItems init { // 初始化时加载数据 // loadData() // 初始化时加载包裹列表数据 loadExpressData() // 初始化时不加载财务列表数据 0317 // loadFinanceData() } // 加载数据的方法 // fun loadData() { // // 获取数据,并更新 LiveData // _codeList.value = Core.code.getAllDesc() // 假设这是获取最新的 data 的方法 // } // // // 如果需要手动更新数据,可以调用这个方法 // fun updateData() { // _codeList.value = Core.code.getAllDesc() // 重新获取并更新数据 // } fun loadExpressData() { viewModelScope.launch { // 1. 获取所有驿站类型的提醒设置 val stations = Core.reminder.getByType("驿站") // 2. 按驿站分组获取包裹信息 val groups = stations.map { station -> val packages = Core.code.getByKeyword(station.nickname).map { code -> @@ -57,13 +42,36 @@ ) } ExpressGroup( stationName = station.nickname, packages = packages stationName = station.nickname, packages = packages ) } _expressItems.postValue(groups) } } fun loadFinanceData() { viewModelScope.launch { // 1. 获取所有驿站类型的提醒设置 val stations = Core.reminder.getByType("财务") // 2. 按驿站分组获取包裹信息 val groups = stations.map { station -> val packages = Core.code.getByKeyword(station.nickname).map { code -> FinancePackage( id = code.id, //ID company = code.name, //快递公司 trackingNumber = code.code, // 取件码 createTime = code.createtime //快递时间 ) } FinanceGroup( stationName = station.nickname, packages = packages ) } _financeItems.postValue(groups) } } } app/src/main/res/layout/activity_phone_login.xml
@@ -67,7 +67,7 @@ android:layout_weight="1" android:background="@null" android:hint="请输入验证码" android:text="123456" android:text="888888" android:inputType="number" android:maxLength="6" android:textSize="16sp" app/src/main/res/layout/fragment_home.xml
@@ -106,64 +106,6 @@ android:layout_height="wrap_content" android:padding="8dp"/> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" android:padding="16dp"> <!-- 花呗还款 --> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="花呗" android:textSize="16sp" android:textStyle="bold" android:layout_marginBottom="8dp"/> <TextView android:id="@+id/huabeiAmount" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="230,721 元" android:textSize="24sp" android:textColor="#FF5722" android:layout_marginBottom="4dp"/> <TextView android:id="@+id/huabeiDueDate" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="提醒您2025.01.14 还款" android:textSize="14sp" android:layout_marginBottom="16dp"/> <!-- 信用卡还款 --> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="中国银行信用卡" android:textSize="16sp" android:textStyle="bold" android:layout_marginBottom="8dp"/> <TextView android:id="@+id/creditCardAmount" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="310,621 元" android:textSize="24sp" android:textColor="#FF5722" android:layout_marginBottom="4dp"/> <TextView android:id="@+id/creditCardDueDate" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="提醒您2025.02.10 还款" android:textSize="14sp"/> </LinearLayout> </androidx.cardview.widget.CardView> </FrameLayout> app/src/main/res/layout/item_finance.xml
对比新文件 @@ -0,0 +1,59 @@ <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" android:padding="12dp" android:gravity="center_vertical"> <ImageView android:id="@+id/iv_package_status" android:layout_width="12dp" android:layout_height="12dp" android:src="@drawable/circle"/> <TextView android:id="@+id/tv_package_id" android:layout_width="0dp" android:layout_height="0dp" android:visibility="gone"/> <ImageView android:id="@+id/iv_company_logo" android:layout_width="20dp" android:layout_height="20dp" android:layout_marginEnd="6dp"/> <LinearLayout android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:orientation="vertical"> <TextView android:id="@+id/tv_company" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textSize="12sp" android:textColor="#333333" android:textStyle="bold"/> <TextView android:id="@+id/tv_create_time" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textSize="12sp" android:textColor="#666666" android:layout_marginTop="4dp"/> </LinearLayout> <TextView android:id="@+id/tv_tracking_number" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textSize="25sp" android:textColor="#333333" android:layout_marginStart="12dp" android:textStyle="bold"/> </LinearLayout> app/src/main/res/layout/item_finance_group.xml
对比新文件 @@ -0,0 +1,54 @@ <?xml version="1.0" encoding="utf-8"?> <androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginHorizontal="12dp" android:layout_marginVertical="6dp" app:cardCornerRadius="8dp" app:cardElevation="2dp"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" android:padding="16dp"> <!-- 驿站名称和包裹数量 --> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" android:layout_marginBottom="12dp"> <ImageView android:id="@+id/iv_station_icon" android:layout_width="20dp" android:layout_height="20dp" android:layout_gravity="center_vertical" android:src="@drawable/location"/> <TextView android:id="@+id/tv_station_name" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:layout_marginStart="8dp" android:textSize="16sp" android:textColor="#333333"/> <TextView android:id="@+id/tv_package_count" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textSize="14sp" android:textColor="#666666"/> </LinearLayout> <!-- 包裹列表 --> <androidx.recyclerview.widget.RecyclerView android:id="@+id/rv_packages" android:layout_width="match_parent" android:layout_height="wrap_content"/> </LinearLayout> </androidx.cardview.widget.CardView> app/src/main/res/layout/item_finance_package_home.xml
对比新文件 @@ -0,0 +1,47 @@ <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" android:padding="12dp" android:gravity="center_vertical"> <ImageView android:id="@+id/iv_company_logo" android:layout_width="20dp" android:layout_height="20dp" android:layout_marginEnd="6dp"/> <LinearLayout android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:orientation="vertical"> <TextView android:id="@+id/tv_company" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textSize="12sp" android:textColor="#333333" android:textStyle="bold"/> <TextView android:id="@+id/tv_create_time" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textSize="12sp" android:textColor="#666666" android:layout_marginTop="4dp"/> </LinearLayout> <TextView android:id="@+id/tv_tracking_number" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textSize="25sp" android:textColor="#333333" android:layout_marginStart="12dp" android:textStyle="bold"/> </LinearLayout> app/src/main/res/values/colors.xml
@@ -15,4 +15,5 @@ <color name="gray">#757575</color> <color name="light_blue_50">#E1F5FE</color> <color name="tab_selected">#FF039BE5</color> <color name="light_blue_new">#02A7F0</color> </resources> app/src/main/res/values/themes.xml
@@ -3,8 +3,8 @@ <!-- <style name="Theme.FirstApp" parent="Theme.MaterialComponents.DayNight.DarkActionBar">--> <style name="Theme.FirstApp" parent="Theme.MaterialComponents.DayNight.DarkActionBar"> <!-- Primary brand color. --> <item name="colorPrimary">@color/purple_500</item> <item name="colorPrimaryVariant">@color/purple_700</item> <item name="colorPrimary">@color/light_blue_new</item> <item name="colorPrimaryVariant">@color/light_blue_new</item> <item name="colorOnPrimary">@color/white</item> <!-- Secondary brand color. --> <item name="colorSecondary">@color/teal_200</item>