From dde2eeddbaf07246da7c2c352e7d49ec63fddcd2 Mon Sep 17 00:00:00 2001 From: tj <1378534974@qq.com> Date: 星期五, 28 三月 2025 14:56:29 +0800 Subject: [PATCH] Merge remote-tracking branch 'origin/master' --- app/src/main/java/com/example/firstapp/entity/InvitationRecord.kt | 6 app/src/main/java/com/example/firstapp/database/response/SmsProcessResponse.kt | 31 + app/src/main/res/layout/activity_invitation_main.xml | 235 +++++++++++ app/src/main/java/com/example/firstapp/database/dao/CodeDao.kt | 50 +- app/src/main/res/layout/activity_invitation_success.xml | 21 + app/src/main/java/com/example/firstapp/adapter/PackageAdapter.kt | 6 app/src/main/res/layout/item_finance_package_home.xml | 2 app/src/main/java/com/example/firstapp/ui/home/HomeViewModel.kt | 12 app/src/main/java/com/example/firstapp/model/CourierStat.kt | 4 app/src/main/java/com/example/firstapp/adapter/MyAdapter.kt | 4 app/src/main/java/com/example/firstapp/ui/notifications/NotificationsFragment.kt | 62 +- app/src/main/java/com/example/firstapp/MainActivity.kt | 253 ++++++++--- app/src/main/res/layout/item_finance_group.xml | 10 app/src/main/java/com/example/firstapp/ui/reminder/ReminderSettingsFragment.kt | 2 app/src/main/java/com/example/firstapp/activity/PickupActivity.kt | 4 app/src/main/java/com/example/firstapp/receiver/SmsReceiver.kt | 219 ++++------ app/src/main/AndroidManifest.xml | 6 app/src/main/java/com/example/firstapp/database/entity/Code.kt | 16 app/src/main/java/com/example/firstapp/database/service/ApiService.kt | 8 app/src/main/res/layout/fragment_home.xml | 4 app/src/main/res/layout/fragment_notifications.xml | 76 +- app/src/main/java/com/example/firstapp/ui/invitation/InvitationActivity.kt | 102 +++++ app/src/main/res/drawable/avatar.png | 0 app/src/main/java/com/example/firstapp/model/DailyStat.kt | 4 app/src/main/res/drawable/rounded_corner_image.xml | 5 app/src/main/res/drawable/gift_one.png | 0 app/src/main/java/com/example/firstapp/adapter/InvitationAdapter.kt | 36 + app/src/main/java/com/example/firstapp/ui/invitation/LineByLineSmoothScroller.kt | 13 28 files changed, 859 insertions(+), 332 deletions(-) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 83d5efc..99381b1 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -119,7 +119,11 @@ android:theme="@style/Theme.ContentDetail" /> <activity android:name=".ui.profile.EditProfileActivity" - android:exported="false" /> + android:exported="false"/> + <activity + android:name=".ui.invitation.InvitationActivity" + android:exported="false"/> + </application> </manifest> \ No newline at end of file diff --git a/app/src/main/java/com/example/firstapp/MainActivity.kt b/app/src/main/java/com/example/firstapp/MainActivity.kt index 7d01490..8b689c9 100644 --- a/app/src/main/java/com/example/firstapp/MainActivity.kt +++ b/app/src/main/java/com/example/firstapp/MainActivity.kt @@ -5,23 +5,18 @@ 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 @@ -31,7 +26,6 @@ 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.entity.Rule import com.example.firstapp.ui.home.HomeViewModel import com.example.firstapp.utils.Log import com.example.firstapp.workers.KeywordUpdateWorker @@ -40,41 +34,48 @@ import kotlinx.coroutines.launch import java.text.SimpleDateFormat import java.time.LocalDateTime -import java.time.format.DateTimeFormatter 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() + 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() - } - else -> { - // 有权限被拒绝 - Toast.makeText(this, "需要短信读取和接收权限才能正常使用功能", Toast.LENGTH_SHORT).show() +// initializeSecurityKeywords() + } + + 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) @@ -95,13 +96,18 @@ navView.setupWithNavController(navController) // 检查权限 - 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) { + 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 - )) + multiplePermissionRequest.launch( + arrayOf( + Manifest.permission.RECEIVE_SMS, Manifest.permission.READ_SMS + ) + ) } else { // 权限已经授予,继续执行相关操作 registerSmsReceiver() @@ -178,10 +184,7 @@ 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)) @@ -189,19 +192,24 @@ } // 初始化禁用词 + @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") + android.util.Log.d( + "MainActivity", "securityKeywordsList: $securityKeywordsList" + ) // 确保在主线程中调用 syncRecentSms runOnUiThread { syncRecentSms() } } else { - android.util.Log.e("MainActivity", "Failed to get security list: ${response.code}") + 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) @@ -209,15 +217,14 @@ } } + @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 >= ?", @@ -227,50 +234,146 @@ cursor?.use { while (cursor.moveToNext()) { - //手机号 - val address = cursor.getString(cursor.getColumnIndexOrThrow("address")) - //短信内容 val messageBody = cursor.getString(cursor.getColumnIndexOrThrow("body")) - //短信时间 val datetime = cursor.getLong(cursor.getColumnIndexOrThrow("date")) - // 这里我要写个数组,并创建个对象存放一些内容,如这个对象的属性有匹配内容,正则表达式,并循环遍历 - val ruleList = mutableListOf( - Rule("快递","京东","\\d{6}"), - Rule("快递","菜鸟","\\d{1,2}-\\d{1,2}-\\d{4}") - ) - for (rule in ruleList) { - val code = rule.extractCodeFromMessage(messageBody.toString()) - if (code!==null) { + + // 转换为 Date 对象 + val date = Date(datetime) + val sdf = SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault()) + val dateString = sdf.format(date) - // 转换为 Date 对象 - val date = Date(datetime) - // 如果需要格式化显示 - val sdf = SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault()) - val dateString = sdf.format(date) - val existingCode = Core.code.queryByTypeAndCodeAndDate(rule.content, code, dateString) - if (existingCode == null) { - android.util.Log.d("SmsReceiver", "Received SMS code: ${code}") - val msg = Msg(0, "1111", "111111", messageBody.toString(), 1, "111", 1, 1) - val msgId = Core.msg.insert(msg) - // 禁用关键词拦截,如果有禁用词则不保存在Code里面 - android.util.Log.d("首页SmsReceiver", "securityKeywordsList: $securityKeywordsList") - if (securityKeywordsList.any { it in messageBody.toString() }) { - android.util.Log.d("首页SmsReceiver", "Received SMS code: 禁用关键词拦截,不保存") - continue + // 保存原始短信 + 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 = RetrofitClient.apiService.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) } - val code = Code(0, rule.type, 1, rule.content, 1, 1, msgId, code, dateString, "中通",0,"","") - Core.code.insert(code) - android.util.Log.d("SMS_DEBUG", "历史短信已保存到数据库") - }else{ - android.util.Log.d("SmsReceiver", "Received SMS code: 已存在相同记录,不保存") } - }else{ - android.util.Log.d("SmsReceiver", "Received SMS code: 没有匹配到历史短信内容") + } catch (e: Exception) { + android.util.Log.e("MainActivity", "处理历史短信出错: ${e.message}", e) } } - // 发送广播通知数据已更新 - val updateIntent = Intent("com.example.firstapp.DATA_UPDATED") - sendBroadcast(updateIntent) } } } catch (e: Exception) { diff --git a/app/src/main/java/com/example/firstapp/activity/PickupActivity.kt b/app/src/main/java/com/example/firstapp/activity/PickupActivity.kt index 2ba9cd2..d7dda6d 100644 --- a/app/src/main/java/com/example/firstapp/activity/PickupActivity.kt +++ b/app/src/main/java/com/example/firstapp/activity/PickupActivity.kt @@ -104,9 +104,9 @@ val packages = codes.map { code -> ExpressPackage( id = code.id, - company = code.name ?: company, + company = code.secondLevel ?: company, trackingNumber = code.code, - createTime = code.createtime + createTime = code.createTime ) } diff --git a/app/src/main/java/com/example/firstapp/adapter/InvitationAdapter.kt b/app/src/main/java/com/example/firstapp/adapter/InvitationAdapter.kt new file mode 100644 index 0000000..b045175 --- /dev/null +++ b/app/src/main/java/com/example/firstapp/adapter/InvitationAdapter.kt @@ -0,0 +1,36 @@ +package com.example.firstapp.adapter + +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.TextView +import androidx.recyclerview.widget.RecyclerView +import com.example.firstapp.R +import com.example.firstapp.entity.InvitationRecord + +class InvitationAdapter : RecyclerView.Adapter<InvitationAdapter.ViewHolder>() { + + private var records = emptyList<InvitationRecord>() + + class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { + val message: TextView = itemView.findViewById(R.id.message) + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { + val view = LayoutInflater.from(parent.context) + .inflate(R.layout.activity_invitation_success, parent, false) + return ViewHolder(view) + } + + override fun onBindViewHolder(holder: ViewHolder, position: Int) { + val record = records[position] + holder.message.text = "${record.userName}邀请好友,${record.reward}" + } + + override fun getItemCount() = records.size + + fun submitList(newList: List<InvitationRecord>) { + records = newList + notifyDataSetChanged() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/firstapp/adapter/MyAdapter.kt b/app/src/main/java/com/example/firstapp/adapter/MyAdapter.kt index 6bc68f0..330ebb9 100644 --- a/app/src/main/java/com/example/firstapp/adapter/MyAdapter.kt +++ b/app/src/main/java/com/example/firstapp/adapter/MyAdapter.kt @@ -26,9 +26,9 @@ override fun onBindViewHolder(holder: ViewHolder, position: Int) { val item = getItem(position) // 使用 getItem 来获取当前位置的 item - holder.tvTitle.text = item.type // 假设 Code 类有一个 `type` 属性 + holder.tvTitle.text = item.oneLevel // 假设 Code 类有一个 `type` 属性 holder.tvDescription.text = item.code // 假设 Code 类有一个 `code` 属性 - var createtime = "请注意:当前取件免费截止时间是"+item.createtime+",超时会收取额外费用" + var createtime = "请注意:当前取件免费截止时间是"+item.createTime+",超时会收取额外费用" holder.overTimeMsg.text = createtime println("打印......") } diff --git a/app/src/main/java/com/example/firstapp/adapter/PackageAdapter.kt b/app/src/main/java/com/example/firstapp/adapter/PackageAdapter.kt index 21edc63..dc3df25 100644 --- a/app/src/main/java/com/example/firstapp/adapter/PackageAdapter.kt +++ b/app/src/main/java/com/example/firstapp/adapter/PackageAdapter.kt @@ -45,13 +45,13 @@ fun bind(code: Code) { // imgCourier.setImageResource(code.category) - textCourierName.text = code.type + textCourierName.text = code.oneLevel textTrackingNumber.text = code.code // 步骤1:定义解析器,将字符串转为 Date val parser = SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault()) val formatter = SimpleDateFormat("yyyy-MM-dd HH:mm", Locale.getDefault()) try { - val date: Date? = parser.parse(code.createtime) // 解析字符串 + val date: Date? = parser.parse(code.createTime) // 解析字符串 date?.let { textTime.text = "到货:"+formatter.format(it) // 格式化并赋值 } ?: run { @@ -63,7 +63,7 @@ textTime.text = "Format Error" } try { - val date2: Date? = parser.parse(code.pickuptime) // 解析字符串 + val date2: Date? = parser.parse(code.pickupTime) // 解析字符串 date2?.let { textPickTime.text = "取件:"+formatter.format(it) // 格式化并赋值 } ?: run { diff --git a/app/src/main/java/com/example/firstapp/database/dao/CodeDao.kt b/app/src/main/java/com/example/firstapp/database/dao/CodeDao.kt index e7dc073..f79e67a 100644 --- a/app/src/main/java/com/example/firstapp/database/dao/CodeDao.kt +++ b/app/src/main/java/com/example/firstapp/database/dao/CodeDao.kt @@ -35,7 +35,7 @@ @Query("SELECT * FROM Code") fun getAllCodes(): List<Code> - @Query("SELECT * FROM Code WHERE type = :type") + @Query("SELECT * FROM Code WHERE oneLevel = :type") fun getCodesByType(type: String): List<Code> @@ -48,13 +48,13 @@ @Query(""" SELECT * FROM Code - WHERE type LIKE '%' || :keyword || '%' + WHERE oneLevel LIKE '%' || :keyword || '%' AND pickup = '0' ORDER BY time DESC """) fun getByKeyword(keyword: String): List<Code> - @Query("SELECT * FROM Code WHERE type = :content and code= :code and createtime = :dateString LIMIT 1") + @Query("SELECT * FROM Code WHERE oneLevel = :content and code= :code and createTime = :dateString LIMIT 1") fun queryByTypeAndCodeAndDate(content: String, code: String, dateString: String): Code @@ -62,23 +62,23 @@ fun pickup(id: Long) //查询当天包裹信息 - @Query("SELECT * FROM code WHERE date(createtime) = date(:date/1000, 'unixepoch', 'localtime') ORDER BY createtime DESC") + @Query("SELECT * FROM code WHERE date(createTime) = date(:date/1000, 'unixepoch', 'localtime') ORDER BY createTime DESC") fun getNewPackagesByDay(date: Long): List<Code> @Query(""" SELECT * FROM code - WHERE substr(createtime, 1, 10) = + WHERE substr(createTime, 1, 10) = date(:date/1000, 'unixepoch', 'localtime') - ORDER BY createtime DESC + ORDER BY createTime DESC """) fun getPackagesByDay(date: Long): Flow<List<Code>> @Query(""" - SELECT type as courierName, COUNT(*) as count + SELECT oneLevel as courierName, COUNT(*) as count FROM code - WHERE strftime('%Y-%W', substr(createtime, 1, 10)) = + WHERE strftime('%Y-%W', substr(createTime, 1, 10)) = strftime('%Y-%W', datetime(:date/1000, 'unixepoch', 'localtime')) - GROUP BY type + GROUP BY oneLevel ORDER BY count DESC """) fun getCourierStatsByWeek(date: Long): Flow<List<CourierStat>> @@ -98,7 +98,7 @@ COUNT(c.id) as count, date_value as weekStart FROM dates d - LEFT JOIN code c ON strftime('%Y-%m-%d', c.createtime) BETWEEN + LEFT JOIN code c ON strftime('%Y-%m-%d', c.createTime) BETWEEN strftime('%Y-%m-%d', d.date_value) AND strftime('%Y-%m-%d', date(d.date_value, '+6 days')) GROUP BY d.date_value @@ -109,28 +109,28 @@ @Query(""" SELECT * FROM code - WHERE substr(createtime, 1, 10) = + WHERE substr(createTime, 1, 10) = date(:date/1000, 'unixepoch', 'localtime') - ORDER BY createtime DESC + ORDER BY createTime DESC """) fun getPackagesByWeek(date: Long): Flow<List<Code>> @Query(""" - SELECT type as courierName, COUNT(*) as count + SELECT oneLevel as courierName, COUNT(*) as count FROM code - WHERE strftime('%Y-%m', substr(createtime, 1, 10)) = + WHERE strftime('%Y-%m', substr(createTime, 1, 10)) = strftime('%Y-%m', datetime(:date/1000, 'unixepoch', 'localtime')) - GROUP BY type + GROUP BY oneLevel ORDER BY count DESC """) fun getCourierStatsByMonth(date: Long): Flow<List<CourierStat>> @Query(""" - SELECT type as courierName, COUNT(*) as count + SELECT oneLevel as courierName, COUNT(*) as count FROM code - WHERE strftime('%Y', substr(createtime, 1, 10)) = + WHERE strftime('%Y', substr(createTime, 1, 10)) = strftime('%Y', datetime(:date/1000, 'unixepoch', 'localtime')) - GROUP BY type + GROUP BY oneLevel ORDER BY count DESC """) fun getCourierStatsByYear(date: Long): Flow<List<CourierStat>> @@ -148,7 +148,7 @@ // COUNT(code.id) as count, // month_start as week_start // FROM months -// LEFT JOIN code ON strftime('%Y-%m', code.createtime) = strftime('%Y-%m', months.month_start) +// LEFT JOIN code ON strftime('%Y-%m', code.createTime) = strftime('%Y-%m', months.month_start) // GROUP BY months.month_start // ORDER BY months.month_start ASC // """) @@ -167,7 +167,7 @@ COUNT(code.id) as count, year_start as week_start FROM years - LEFT JOIN code ON strftime('%Y', code.createtime) = strftime('%Y', years.year_start) + LEFT JOIN code ON strftime('%Y', code.createTime) = strftime('%Y', years.year_start) GROUP BY years.year_start ORDER BY years.year_start ASC """) @@ -186,7 +186,7 @@ CAST(strftime('%W', dates.date) AS INTEGER) as weekOfYear, COUNT(code.id) as count FROM dates - LEFT JOIN code ON date(code.createtime) = dates.date + LEFT JOIN code ON date(code.createTime) = dates.date GROUP BY dates.date ORDER BY dates.date """) @@ -205,7 +205,7 @@ COUNT(code.id) as count, days.date as week_start FROM days - LEFT JOIN code ON date(code.createtime) = days.date + LEFT JOIN code ON date(code.createTime) = days.date GROUP BY days.date ORDER BY days.date ASC """) @@ -224,7 +224,7 @@ COUNT(code.id) as count, month_start as weekStart FROM months - LEFT JOIN code ON strftime('%Y-%m', code.createtime) = strftime('%Y-%m', months.month_start) + LEFT JOIN code ON strftime('%Y-%m', code.createTime) = strftime('%Y-%m', months.month_start) GROUP BY months.month_start ORDER BY months.month_start ASC """) @@ -245,7 +245,7 @@ COUNT(code.id) as count, days.date as weekStart FROM days - LEFT JOIN code ON date(code.createtime) = days.date + LEFT JOIN code ON date(code.createTime) = days.date GROUP BY days.date ORDER BY days.date ASC """) @@ -258,7 +258,7 @@ COUNT(id) as count, datetime(:date/1000, 'unixepoch', 'localtime') as weekStart FROM code - WHERE strftime('%Y', createtime) = strftime('%Y', datetime(:date/1000, 'unixepoch', 'localtime')) + WHERE strftime('%Y', createTime) = strftime('%Y', datetime(:date/1000, 'unixepoch', 'localtime')) """) fun getCurrentYearStats(date: Long): Flow<List<DailyStat>> } diff --git a/app/src/main/java/com/example/firstapp/database/entity/Code.kt b/app/src/main/java/com/example/firstapp/database/entity/Code.kt index 40a1ac5..93d0e8a 100644 --- a/app/src/main/java/com/example/firstapp/database/entity/Code.kt +++ b/app/src/main/java/com/example/firstapp/database/entity/Code.kt @@ -9,15 +9,17 @@ @PrimaryKey(autoGenerate = true) val id: Long = 0, // 自增长的 id val category: String, val categoryId: Long, - val type: String, val typeId: Long, val ruleId: Long, val msgId: Long, - val code: String, - var createtime: String, - var name: String, - var pickup: Int, - var pickuptime: String, //取件时间 - var overtime: String, // 超期时间 + var createTime: String, + val oneLevel: String, //type改成oneLevel + var secondLevel: String, //name改成secondLevel + val code: String, //快递对应取件码、还款对应金额 + var pickup: Int, //快递取件状态 + var pickupTime: String, //快递取件时间 + var overTime: String, //快递超期时间、还款日期 + var address: String, //地址信息 + var remarks: String, //备注信息保存 var time: Date = Date(), ) diff --git a/app/src/main/java/com/example/firstapp/database/response/SmsProcessResponse.kt b/app/src/main/java/com/example/firstapp/database/response/SmsProcessResponse.kt new file mode 100644 index 0000000..523f7aa --- /dev/null +++ b/app/src/main/java/com/example/firstapp/database/response/SmsProcessResponse.kt @@ -0,0 +1,31 @@ +package com.example.firstapp.database.response + +import com.google.gson.annotations.SerializedName + +// 添加数据类来映射API响应 +data class SmsProcessResponse( + val status: String, + val data: ProcessedData +) + +data class ProcessedData( + val category: String, + val details: Details +) + +data class Details( + val post: String?, + val company: String?, + @SerializedName("pickup_code") + val pickupCode: String?, + val type: String?, + val bank: String?, + val amount: String?, + val date: String?, + val address: String?, + val time: String?, + val min_amount: String?, + val number: String?, + val datetime: String?, + val balance: String?, +) \ No newline at end of file diff --git a/app/src/main/java/com/example/firstapp/database/service/ApiService.kt b/app/src/main/java/com/example/firstapp/database/service/ApiService.kt index d38137e..3171ff6 100644 --- a/app/src/main/java/com/example/firstapp/database/service/ApiService.kt +++ b/app/src/main/java/com/example/firstapp/database/service/ApiService.kt @@ -7,11 +7,13 @@ import com.example.firstapp.database.response.DictResponse import com.example.firstapp.database.response.LoginResponse import com.example.firstapp.database.response.SecurityResponse +import com.example.firstapp.database.response.SmsProcessResponse import com.example.firstapp.database.response.UserInfo import okhttp3.MultipartBody import okhttp3.RequestBody import retrofit2.Retrofit import retrofit2.converter.gson.GsonConverterFactory +import retrofit2.http.Body import retrofit2.http.GET import retrofit2.http.Multipart import retrofit2.http.POST @@ -54,12 +56,16 @@ @Part("nickname") nickname: RequestBody, @Part avatar: MultipartBody.Part? ): ApiResponse<Unit> + + @POST("process-sms") + suspend fun processSms(@Body body: Map<String, String>): SmsProcessResponse } // 创建Retrofit实例(单例) object RetrofitClient{ - private const val BASE_URL ="http://192.168.1.198:8888/jshERP-boot/" +// private const val BASE_URL ="http://192.168.1.213:8888/jshERP-boot/" + private const val BASE_URL ="http://192.168.1.213:5000/" //添加Gson解析器,用于自动将JSON响应转换为Kotlin/Java对象 private val retrofit = Retrofit.Builder().baseUrl(BASE_URL).addConverterFactory(GsonConverterFactory.create()).build() diff --git a/app/src/main/java/com/example/firstapp/entity/InvitationRecord.kt b/app/src/main/java/com/example/firstapp/entity/InvitationRecord.kt new file mode 100644 index 0000000..938bac5 --- /dev/null +++ b/app/src/main/java/com/example/firstapp/entity/InvitationRecord.kt @@ -0,0 +1,6 @@ +package com.example.firstapp.entity + +data class InvitationRecord( + val userName: String, + val reward: String +) \ No newline at end of file diff --git a/app/src/main/java/com/example/firstapp/model/CourierStat.kt b/app/src/main/java/com/example/firstapp/model/CourierStat.kt index 7cb6c0a..a745f73 100644 --- a/app/src/main/java/com/example/firstapp/model/CourierStat.kt +++ b/app/src/main/java/com/example/firstapp/model/CourierStat.kt @@ -4,9 +4,9 @@ @DatabaseView( """ - SELECT type as courierName, COUNT(*) as count + SELECT oneLevel as courierName, COUNT(*) as count FROM Code - GROUP BY type + GROUP BY oneLevel """ ) data class CourierStat( diff --git a/app/src/main/java/com/example/firstapp/model/DailyStat.kt b/app/src/main/java/com/example/firstapp/model/DailyStat.kt index b6c7301..86aec16 100644 --- a/app/src/main/java/com/example/firstapp/model/DailyStat.kt +++ b/app/src/main/java/com/example/firstapp/model/DailyStat.kt @@ -4,10 +4,10 @@ @DatabaseView( """ - SELECT substr(createtime, 1, 10) as date, + SELECT substr(createTime, 1, 10) as date, COUNT(*) as count FROM code - GROUP BY substr(createtime, 1, 10) + GROUP BY substr(createTime, 1, 10) """ ) data class DailyStat( diff --git a/app/src/main/java/com/example/firstapp/receiver/SmsReceiver.kt b/app/src/main/java/com/example/firstapp/receiver/SmsReceiver.kt index abfd750..3d732b4 100644 --- a/app/src/main/java/com/example/firstapp/receiver/SmsReceiver.kt +++ b/app/src/main/java/com/example/firstapp/receiver/SmsReceiver.kt @@ -12,16 +12,13 @@ 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.repository.KeywordRepository import com.example.firstapp.database.service.RetrofitClient -import com.example.firstapp.entity.Rule import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import java.text.SimpleDateFormat import java.time.LocalDateTime import java.time.ZoneId -import java.time.format.DateTimeFormatter import java.util.Date import java.util.Locale @@ -33,24 +30,7 @@ @RequiresApi(Build.VERSION_CODES.O) override fun onReceive(context: Context, intent: Intent) { - - // 检查广播的 Action 是否为短信接收 if (Telephony.Sms.Intents.SMS_RECEIVED_ACTION == intent.action) { - - CoroutineScope(Dispatchers.IO).launch { - try { - val response = RetrofitClient.apiService.getSecurityList(); - // 这里需要将response.data存放到变量中 - if(response.code===200){ - securityKeywordsList = response.data.map { it.keyword } - Log.d("SmsReceiver", "securityKeywordsList: $securityKeywordsList") - } - }catch (e: Exception){ - Log.d("SmsReceiver", "Error: ${e.message}") - } - } - - // 获取短信内容 val bundle: Bundle? = intent.extras bundle?.let { val pdus = it.get("pdus") as Array<*> @@ -62,8 +42,8 @@ messageBody.append(messages[i]?.messageBody) } - // 输出短信内容到控制台 - val msg = Msg(0, "1111", "111111", messageBody.toString(),1, "111", 1, 1) + // 保存原始短信 + val msg = Msg(0, "1111", "111111", messageBody.toString(), 1, "111", 1, 1) val msgId = Core.msg.insert(msg) // 这里需要查看消息是否含有securityKeywordsList这个列表中的关键词,如果含有,则不进行下一步操作 @@ -74,125 +54,104 @@ Log.d("SmsReceiver", "运行到正则匹配") - // 这里我要写个数组,并创建个对象存放一些内容,如这个对象的属性有匹配内容,正则表达式,并循环遍历 - val ruleList = mutableListOf( - Rule("快递","京东","\\d{6}"), - 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})[日号]") - ) - + // 调用API处理短信 CoroutineScope(Dispatchers.IO).launch { - Log.d("SmsReceiver", "CoroutineScope started") - // 获取最新的关键词配置 - val keywords = Core.keyword.getKeywords() - Log.d("keywords", keywords.toString()) - keywords.forEach { keyword -> - ruleList.add( - Rule( - keyword.type, - keyword.keyword, - "\\d{1,2}-\\d{1,2}-\\d{4}" - ) - ) - } - Log.d("RuleList", ruleList.toString()) + try { + val response = + RetrofitClient.apiService.processSms(mapOf("content" to messageBody.toString())) - for (rule in ruleList) { - 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) { + if (response.status == "success") { + // 获取当前时间 val currentTime = LocalDateTime.now() - val date = Date.from(currentTime.atZone(ZoneId.systemDefault()).toInstant()) + 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 + // 根据不同类型处理数据 + when (response.data.category) { + "快递" -> { + val code = Code( + id = 0, + category = response.data.category, + categoryId = 1, // 1-快递类型 + typeId = 1, //暂时没有根据type分类 + ruleId = 1, //1-快递类型 + msgId = msgId, + createTime = createtime, + oneLevel = response.data.details.post ?: "", + secondLevel = response.data.details.company ?: "", + code = response.data.details.pickupCode ?: "", + pickup = 0, // 0-未取件,1-已取件 + pickupTime = "", // 取件时间为空 + overTime = "", // 超时时间为空,暂时没有这块处理逻辑 + address = response.data.details.address ?: "", + remarks = response.data.details.time ?: "", + ) + if(code.oneLevel!="" && code.secondLevel!="" && code.code!="") { + Core.code.insert(code) + } + } + + "还款" -> { + val code = Code( + id = 0, + category = response.data.category, + categoryId = 2, // 2-还款类型 + typeId = 1, //暂时没有根据type分类 + ruleId = 2, //1-还款类型 + msgId = msgId, + createTime = createtime, + oneLevel = response.data.details.type ?: "", + secondLevel = response.data.details.bank ?: "", + code = response.data.details.amount ?: "", + pickup = 0, // 0-未取件,1-已取件 + 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) + } + } + + "收入" -> { + val code = Code( + id = 0, + category = response.data.category, + categoryId = 3, // 3-收入类型 + typeId = 1, //暂时没有根据type分类 + ruleId = 2, //1-还款类型 + msgId = msgId, + createTime = createtime, + 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) + } + } } - val existingCode = Core.code.queryByTypeAndCodeAndDate(rule.content, code, createtime) - if (existingCode == null) { - 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", "新短信已保存到数据库") - - // 发送广播通知数据已更新 - //"com.example.firstapp.DATA_UPDATED" 是一个自定义的广播 Action,相当于一个标识符或者说是一个频道名称。这个名称是我们自己定义的,通常使用应用的包名作为前缀,以避免与其他应用的广播冲突。 - val updateIntent = Intent("com.example.firstapp.DATA_UPDATED") - context.sendBroadcast(updateIntent) - Log.d("SMS_DEBUG", "发送数据更新广播") - }else{ - Log.d("SmsReceiver", "Received SMS code: 已存在相同记录,不保存") - } - }else{ - Log.d("SmsReceiver", "Received SMS code: 没有匹配到内容") + // 发送广播通知数据已更新 + val updateIntent = Intent("com.example.firstapp.DATA_UPDATED") + context.sendBroadcast(updateIntent) + Log.d("SMS_DEBUG", "新短信已保存到数据库") } + } catch (e: Exception) { + Log.e("SmsReceiver", "Error processing SMS", e) } } - - - // kotlin 怎么创建一个类 -// for (rule in ruleList) { -// val code = rule.extractCodeFromMessage(messageBody.toString()) -// -// if (code!==null) { -// Log.d("SmsReceiver", "Received SMS code: ${code}") -// -// -// // 获取当前时间 -// val currentTime = LocalDateTime.now() -// // 加2小时 -// val futureTime = currentTime.plusHours(2) -// // 定义时间格式 -// val formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss") -// // 转换为字符串 -// val overtime = futureTime.format(formatter) -// // 封装成一个Code对象,并保存在数据库中 -// val code = Code(0, rule.type,1, rule.content,1, 1, msgId, code, overtime) -// Core.code.insert(code) -// Log.d("SMS_DEBUG", "新短信已保存到数据库") -// // 发送广播通知数据已更新 -// //"com.example.firstapp.DATA_UPDATED" 是一个自定义的广播 Action,相当于一个标识符或者说是一个频道名称。这个名称是我们自己定义的,通常使用应用的包名作为前缀,以避免与其他应用的广播冲突。 -// val updateIntent = Intent("com.example.firstapp.DATA_UPDATED") -// context.sendBroadcast(updateIntent) -// Log.d("SMS_DEBUG", "发送数据更新广播") -// }else{ -// Log.d("SmsReceiver", "Received SMS code: 没有匹配到内容") -// } -// } - } } } diff --git a/app/src/main/java/com/example/firstapp/ui/home/HomeViewModel.kt b/app/src/main/java/com/example/firstapp/ui/home/HomeViewModel.kt index c8c6c17..7b5c3fb 100644 --- a/app/src/main/java/com/example/firstapp/ui/home/HomeViewModel.kt +++ b/app/src/main/java/com/example/firstapp/ui/home/HomeViewModel.kt @@ -29,16 +29,16 @@ fun loadExpressData() { viewModelScope.launch { // 1. 获取所有驿站类型的提醒设置 - val stations = Core.reminder.getByType("驿站") + val stations = Core.reminder.getByType("快递") // 2. 按驿站分组获取包裹信息 val groups = stations.map { station -> val packages = Core.code.getByKeyword(station.nickname).map { code -> ExpressPackage( id = code.id, //ID - company = code.name, //快递公司 + company = code.secondLevel, //快递公司 trackingNumber = code.code, // 取件码 - createTime = code.createtime //快递时间 + createTime = code.createTime //快递时间 ) } ExpressGroup( @@ -53,16 +53,16 @@ fun loadFinanceData() { viewModelScope.launch { // 1. 获取所有驿站类型的提醒设置 - val stations = Core.reminder.getByType("财务") + 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, //快递公司 + company = code.secondLevel, //快递公司 trackingNumber = code.code, // 取件码 - createTime = code.createtime //快递时间 + createTime = code.createTime //快递时间 ) } FinanceGroup( diff --git a/app/src/main/java/com/example/firstapp/ui/invitation/InvitationActivity.kt b/app/src/main/java/com/example/firstapp/ui/invitation/InvitationActivity.kt new file mode 100644 index 0000000..51fc5fc --- /dev/null +++ b/app/src/main/java/com/example/firstapp/ui/invitation/InvitationActivity.kt @@ -0,0 +1,102 @@ +package com.example.firstapp.ui.invitation + +import android.os.Bundle +import android.os.Handler +import android.os.Looper +import android.util.DisplayMetrics +import androidx.appcompat.app.AppCompatActivity +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.LinearSmoothScroller +import androidx.recyclerview.widget.RecyclerView +import com.example.firstapp.R +import com.example.firstapp.adapter.InvitationAdapter +import com.example.firstapp.entity.InvitationRecord +import java.text.SimpleDateFormat +import java.util.Locale +import java.util.concurrent.Executors + +class InvitationActivity : AppCompatActivity() { + + private lateinit var recyclerView: RecyclerView + private lateinit var adapter: InvitationAdapter + private var currentPosition = 0 + private val handler = Handler(Looper.getMainLooper()) + private lateinit var scrollRunnable: Runnable + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_invitation_main) + + setupRecyclerView() + loadData() + setupAutoScroll() + } + + private fun setupRecyclerView() { + recyclerView = findViewById(R.id.invitationsuccessRecyclerView) + recyclerView.layoutManager = LinearLayoutManager(this).apply { + stackFromEnd = false // 从顶部开始布局 + } + adapter = InvitationAdapter() + recyclerView.adapter = adapter + } + + private fun loadData() { + val mockData = listOf( + InvitationRecord("H****e", "获得了3天会员"), + InvitationRecord("U****r", "获得了7天会员"), + InvitationRecord("A****e", "获得了免广告特权"), + InvitationRecord("B****d", "获得了3天会员"), + InvitationRecord("C****o", "获得了7天会员") + ) + adapter.submitList(mockData) + } + + private fun setupAutoScroll() { + scrollRunnable = object : Runnable { + override fun run() { + if (currentPosition < adapter.itemCount - 1) { + currentPosition++ + smoothScrollToPosition(currentPosition) + } else { + // 滚动到底部后回到顶部 + currentPosition = 0 + recyclerView.scrollToPosition(0) + } + handler.postDelayed(this, 2000) + } + } + startAutoScroll() + } + + private fun smoothScrollToPosition(position: Int) { + val layoutManager = recyclerView.layoutManager as LinearLayoutManager + val smoothScroller = object : LinearSmoothScroller(this) { + override fun getVerticalSnapPreference(): Int = SNAP_TO_START + + override fun calculateSpeedPerPixel(displayMetrics: DisplayMetrics): Float { + return 100f / displayMetrics.densityDpi // 控制滚动速度 + } + } + smoothScroller.targetPosition = position + layoutManager.startSmoothScroll(smoothScroller) + } + + private fun startAutoScroll() { + handler.postDelayed(scrollRunnable, 2000) + } + + private fun stopAutoScroll() { + handler.removeCallbacks(scrollRunnable) + } + + override fun onPause() { + super.onPause() + stopAutoScroll() + } + + override fun onResume() { + super.onResume() + startAutoScroll() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/firstapp/ui/invitation/LineByLineSmoothScroller.kt b/app/src/main/java/com/example/firstapp/ui/invitation/LineByLineSmoothScroller.kt new file mode 100644 index 0000000..d339a03 --- /dev/null +++ b/app/src/main/java/com/example/firstapp/ui/invitation/LineByLineSmoothScroller.kt @@ -0,0 +1,13 @@ +import android.content.Context +import android.util.DisplayMetrics +import androidx.recyclerview.widget.LinearSmoothScroller + +class LineByLineSmoothScroller(context: Context) : LinearSmoothScroller(context) { + override fun calculateSpeedPerPixel(displayMetrics: DisplayMetrics): Float { + return 100f / displayMetrics.densityDpi // 控制滚动速度 + } + + override fun getVerticalSnapPreference(): Int { + return SNAP_TO_START // 从顶部开始对齐 + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/firstapp/ui/notifications/NotificationsFragment.kt b/app/src/main/java/com/example/firstapp/ui/notifications/NotificationsFragment.kt index 9c128a1..6566ceb 100644 --- a/app/src/main/java/com/example/firstapp/ui/notifications/NotificationsFragment.kt +++ b/app/src/main/java/com/example/firstapp/ui/notifications/NotificationsFragment.kt @@ -131,37 +131,37 @@ private fun setupClickListeners() { // 支付插件 - binding.payPlugin.setOnClickListener { - // 跳转到支付插件页面 - lifecycleScope.launch { - try { - val response = RetrofitClient.apiService.getPayOrderInfo() - var orderInfo=response.data - Log.d("AliPayHelper","获取订单信息时: ${response}") - // 这里调用支付宝 - PayAbility.aliPay(requireActivity(), orderInfo, Observer { - when (it.resultStatus) { - "9000" -> { -// Snackbar.make(binding.root, "支付成功", Snackbar.LENGTH_LONG).show() - requireActivity().runOnUiThread { - Toast.makeText(requireContext(), "支付成功", Toast.LENGTH_LONG).show() - } - } - else -> { -// Snackbar.make(binding.root, "支付失败", Snackbar.LENGTH_LONG).show() - requireActivity().runOnUiThread { - Toast.makeText(requireContext(), "支付失败", Toast.LENGTH_LONG).show() - } - - } - } - }) - - } catch (e: Exception) { - Log.d("AliPayHelper","获取订单信息时发生错误: ${e.message}") - } - } - } +// binding.payPlugin.setOnClickListener { +// // 跳转到支付插件页面 +// lifecycleScope.launch { +// try { +// val response = RetrofitClient.apiService.getPayOrderInfo() +// var orderInfo=response.data +// Log.d("AliPayHelper","获取订单信息时: ${response}") +// // 这里调用支付宝 +// PayAbility.aliPay(requireActivity(), orderInfo, Observer { +// when (it.resultStatus) { +// "9000" -> { +//// Snackbar.make(binding.root, "支付成功", Snackbar.LENGTH_LONG).show() +// requireActivity().runOnUiThread { +// Toast.makeText(requireContext(), "支付成功", Toast.LENGTH_LONG).show() +// } +// } +// else -> { +//// Snackbar.make(binding.root, "支付失败", Snackbar.LENGTH_LONG).show() +// requireActivity().runOnUiThread { +// Toast.makeText(requireContext(), "支付失败", Toast.LENGTH_LONG).show() +// } +// +// } +// } +// }) +// +// } catch (e: Exception) { +// Log.d("AliPayHelper","获取订单信息时发生错误: ${e.message}") +// } +// } +// } // 设置提醒 diff --git a/app/src/main/java/com/example/firstapp/ui/reminder/ReminderSettingsFragment.kt b/app/src/main/java/com/example/firstapp/ui/reminder/ReminderSettingsFragment.kt index 99f23a4..b0b459c 100644 --- a/app/src/main/java/com/example/firstapp/ui/reminder/ReminderSettingsFragment.kt +++ b/app/src/main/java/com/example/firstapp/ui/reminder/ReminderSettingsFragment.kt @@ -48,7 +48,7 @@ } private fun setupSpinner() { - val types = arrayOf("驿站", "财务", "其他") + val types = arrayOf("快递", "还款", "其他") val adapter = ArrayAdapter(requireContext(), android.R.layout.simple_spinner_item, types) adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item) binding.spinnerType.adapter = adapter diff --git a/app/src/main/res/drawable/avatar.png b/app/src/main/res/drawable/avatar.png new file mode 100644 index 0000000..04a43e1 --- /dev/null +++ b/app/src/main/res/drawable/avatar.png Binary files differ diff --git a/app/src/main/res/drawable/gift_one.png b/app/src/main/res/drawable/gift_one.png new file mode 100644 index 0000000..d6bd848 --- /dev/null +++ b/app/src/main/res/drawable/gift_one.png Binary files differ diff --git a/app/src/main/res/drawable/rounded_corner_image.xml b/app/src/main/res/drawable/rounded_corner_image.xml new file mode 100644 index 0000000..0f52b53 --- /dev/null +++ b/app/src/main/res/drawable/rounded_corner_image.xml @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="utf-8"?> +<shape xmlns:android="http://schemas.android.com/apk/res/android"> + <solid android:color="@android:color/transparent" /> + <corners android:radius="10dp" /> +</shape> \ No newline at end of file diff --git a/app/src/main/res/layout/activity_invitation_main.xml b/app/src/main/res/layout/activity_invitation_main.xml new file mode 100644 index 0000000..cdc5a91 --- /dev/null +++ b/app/src/main/res/layout/activity_invitation_main.xml @@ -0,0 +1,235 @@ +<?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="match_parent" + android:orientation="vertical" + android:background="#F0F0F0"> + + <!-- 标题栏 --> + <TextView + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="邀请有礼" + android:textSize="20sp" + android:gravity="center" + android:padding="16dp" + android:textColor="#333333"/> + + <!-- 横幅广告区域 --> + <ImageView + android:layout_width="match_parent" + android:layout_height="200dp" + android:src="@drawable/gift_one" + android:scaleType="fitXY" + android:layout_marginBottom="16dp" + android:background="@drawable/rounded_corner_image" + android:padding="1dp" /> + + <!-- 邀请成功提示区域 --> + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="vertical" + android:background="#FFFFFF" + android:layout_marginBottom="16dp"> + <androidx.recyclerview.widget.RecyclerView + android:id="@+id/invitationsuccessRecyclerView" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:maxHeight="100dp" + android:paddingStart="16dp" + android:paddingEnd="16dp" + android:paddingBottom="16dp" + android:clipToPadding="false" + android:overScrollMode="never"/> + </LinearLayout> + + <!-- 邀请按钮区域 --> + <Button + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="立即邀请好友" + android:backgroundTint="#FF0000" + android:textColor="#FFFFFF" + android:padding="16dp" + android:textSize="16sp" + android:layout_marginBottom="16dp"/> + + <!-- 邀请任务表格区域 --> + <TableLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:padding="16dp" + android:background="#FFFFFF" + android:layout_marginBottom="16dp"> + + <TableRow> + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="邀请人数" + android:textSize="16sp" + android:padding="8dp" + android:textColor="#333333"/> + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="邀请奖励" + android:textSize="16sp" + android:padding="8dp" + android:textColor="#333333"/> + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="被邀请人奖励" + android:textSize="16sp" + android:padding="8dp" + android:textColor="#333333"/> + </TableRow> + + <TableRow> + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="1人" + android:textSize="16sp" + android:padding="8dp" + android:textColor="#333333"/> + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="3天会员" + android:textSize="16sp" + android:padding="8dp" + android:textColor="#333333"/> + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="1天会员" + android:textSize="16sp" + android:padding="8dp" + android:textColor="#333333"/> + </TableRow> + + <TableRow> + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="3人" + android:textSize="16sp" + android:padding="8dp" + android:textColor="#333333"/> + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="免广告特权 (7天)" + android:textSize="16sp" + android:padding="8dp" + android:textColor="#333333"/> + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="1天会员" + android:textSize="16sp" + android:padding="8dp" + android:textColor="#333333"/> + </TableRow> + + <TableRow> + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="5人" + android:textSize="16sp" + android:padding="8dp" + android:textColor="#333333"/> + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="7天会员" + android:textSize="16sp" + android:padding="8dp" + android:textColor="#333333"/> + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="1天会员" + android:textSize="16sp" + android:padding="8dp" + android:textColor="#333333"/> + </TableRow> + </TableLayout> + + <!-- 邀请记录区域 --> + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="vertical" + android:padding="16dp" + android:background="#FFFFFF"> + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="我的邀请记录" + android:textSize="16sp" + android:paddingBottom="8dp" + android:textColor="#333333"/> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="horizontal" + android:paddingBottom="8dp"> + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="Hnnnnne" + android:textSize="16sp" + android:paddingEnd="8dp" + android:textColor="#333333"/> + + <Button + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="待注册" + android:background="#CCCCCC" + android:textColor="#000000" + android:padding="8dp" + android:textSize="14sp"/> + </LinearLayout> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="horizontal"> + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="Hnnnnne" + android:textSize="16sp" + android:paddingEnd="8dp" + android:textColor="#333333"/> + + <Button + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="已注册" + android:background="#008800" + android:textColor="#FFFFFF" + android:padding="8dp" + android:textSize="14sp"/> + </LinearLayout> + </LinearLayout> + +</LinearLayout> \ No newline at end of file diff --git a/app/src/main/res/layout/activity_invitation_success.xml b/app/src/main/res/layout/activity_invitation_success.xml new file mode 100644 index 0000000..7a235b4 --- /dev/null +++ b/app/src/main/res/layout/activity_invitation_success.xml @@ -0,0 +1,21 @@ +<?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:paddingVertical="16dp"> + + <ImageView + android:id="@+id/avatar" + android:layout_width="40dp" + android:layout_height="40dp" + android:src="@drawable/avatar"/> + + <TextView + android:id="@+id/message" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:textSize="16sp" + android:paddingStart="8dp" + android:textColor="#333333"/> +</LinearLayout> \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_home.xml b/app/src/main/res/layout/fragment_home.xml index 82554fe..18a5e11 100644 --- a/app/src/main/res/layout/fragment_home.xml +++ b/app/src/main/res/layout/fragment_home.xml @@ -8,7 +8,7 @@ <ScrollView android:layout_width="match_parent" android:layout_height="match_parent" - android:layout_marginTop="80dp"> <!-- 留出顶部广告位的高度 --> + android:layout_marginTop="40dp"> <!-- 留出顶部广告位的高度 --> > <!-- LinearLayout的作用是按照垂直或者水平方向排列其子视图--> @@ -109,7 +109,7 @@ <ImageView android:id="@+id/adBanner" android:layout_width="match_parent" - android:layout_height="80dp" + android:layout_height="40dp" android:scaleType="centerCrop" android:src="@drawable/up"/> </androidx.cardview.widget.CardView> diff --git a/app/src/main/res/layout/fragment_notifications.xml b/app/src/main/res/layout/fragment_notifications.xml index 6e60a3b..23b5b81 100644 --- a/app/src/main/res/layout/fragment_notifications.xml +++ b/app/src/main/res/layout/fragment_notifications.xml @@ -156,49 +156,49 @@ android:paddingHorizontal="8dp" android:paddingVertical="4dp"> - <!-- 会员 --> - <RelativeLayout - android:id="@+id/memberVip" - style="@style/PluginPay" - android:padding="16dp"> +<!-- <!– 会员 –>--> +<!-- <RelativeLayout--> +<!-- android:id="@+id/memberVip"--> +<!-- style="@style/PluginPay"--> +<!-- android:padding="16dp">--> - <TextView - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_centerVertical="true" - android:text="会员" - android:textColor="@android:color/black" - android:textSize="16sp" /> +<!-- <TextView--> +<!-- android:layout_width="wrap_content"--> +<!-- android:layout_height="wrap_content"--> +<!-- android:layout_centerVertical="true"--> +<!-- android:text="会员"--> +<!-- android:textColor="@android:color/black"--> +<!-- android:textSize="16sp" />--> - <ImageView - android:layout_width="24dp" - android:layout_height="24dp" - android:layout_alignParentEnd="true" - android:layout_centerVertical="true" - android:src="@drawable/right_forward" /> - </RelativeLayout> +<!-- <ImageView--> +<!-- android:layout_width="24dp"--> +<!-- android:layout_height="24dp"--> +<!-- android:layout_alignParentEnd="true"--> +<!-- android:layout_centerVertical="true"--> +<!-- android:src="@drawable/right_forward" />--> +<!-- </RelativeLayout>--> - <!-- 支付 --> - <RelativeLayout - android:id="@+id/payPlugin" - style="@style/PluginPay" - android:padding="16dp"> +<!-- <!– 支付 –>--> +<!-- <RelativeLayout--> +<!-- android:id="@+id/payPlugin"--> +<!-- style="@style/PluginPay"--> +<!-- android:padding="16dp">--> - <TextView - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_centerVertical="true" - android:text="支付" - android:textColor="@android:color/black" - android:textSize="16sp" /> +<!-- <TextView--> +<!-- android:layout_width="wrap_content"--> +<!-- android:layout_height="wrap_content"--> +<!-- android:layout_centerVertical="true"--> +<!-- android:text="支付"--> +<!-- android:textColor="@android:color/black"--> +<!-- android:textSize="16sp" />--> - <ImageView - android:layout_width="24dp" - android:layout_height="24dp" - android:layout_alignParentEnd="true" - android:layout_centerVertical="true" - android:src="@drawable/right_forward" /> - </RelativeLayout> +<!-- <ImageView--> +<!-- android:layout_width="24dp"--> +<!-- android:layout_height="24dp"--> +<!-- android:layout_alignParentEnd="true"--> +<!-- android:layout_centerVertical="true"--> +<!-- android:src="@drawable/right_forward" />--> +<!-- </RelativeLayout>--> <!-- 设置提醒 --> <LinearLayout diff --git a/app/src/main/res/layout/item_finance_group.xml b/app/src/main/res/layout/item_finance_group.xml index 4a13e84..4ead563 100644 --- a/app/src/main/res/layout/item_finance_group.xml +++ b/app/src/main/res/layout/item_finance_group.xml @@ -1,12 +1,16 @@ <?xml version="1.0" encoding="utf-8"?> -<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android" +<com.google.android.material.card.MaterialCardView + 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"> + app:cardElevation="2dp" + app:cardBackgroundColor="@android:color/white" + app:strokeColor="#FF000000" + app:strokeWidth="2dp"> <LinearLayout android:layout_width="match_parent" @@ -51,4 +55,4 @@ android:layout_width="match_parent" android:layout_height="wrap_content"/> </LinearLayout> -</androidx.cardview.widget.CardView> \ No newline at end of file +</com.google.android.material.card.MaterialCardView> \ No newline at end of file diff --git a/app/src/main/res/layout/item_finance_package_home.xml b/app/src/main/res/layout/item_finance_package_home.xml index a172550..ef12f23 100644 --- a/app/src/main/res/layout/item_finance_package_home.xml +++ b/app/src/main/res/layout/item_finance_package_home.xml @@ -30,7 +30,7 @@ android:id="@+id/tv_create_time" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:textSize="12sp" + android:textSize="10sp" android:textColor="#666666" android:layout_marginTop="4dp"/> </LinearLayout> -- Gitblit v1.9.3