| | |
| | | import android.net.Uri |
| | | import android.os.Build |
| | | import androidx.annotation.RequiresApi |
| | | import androidx.work.ExistingPeriodicWorkPolicy |
| | | import androidx.work.PeriodicWorkRequestBuilder |
| | | import androidx.work.WorkManager |
| | | import com.example.firstapp.activity.LoginActivity |
| | | import com.example.firstapp.adapter.MyAdapter |
| | | import com.example.firstapp.core.Core |
| | | import com.example.firstapp.database.entity.Code |
| | | import com.example.firstapp.database.entity.Msg |
| | | import com.example.firstapp.database.service.RetrofitClient |
| | | import com.example.firstapp.database.service.RetrofitModelClient |
| | | import com.example.firstapp.ui.home.HomeViewModel |
| | | import com.example.firstapp.utils.Log |
| | | import com.example.firstapp.workers.KeywordUpdateWorker |
| | | import kotlinx.coroutines.CoroutineScope |
| | | import kotlinx.coroutines.Dispatchers |
| | | import kotlinx.coroutines.launch |
| | | import java.text.SimpleDateFormat |
| | | import java.time.LocalDateTime |
| | | import java.util.Calendar |
| | | import java.util.Date |
| | | import java.util.Locale |
| | | import java.util.concurrent.TimeUnit |
| | | import java.time.ZoneId |
| | | |
| | | class MainActivity : AppCompatActivity() { |
| | | // 安全防护关键词数组 |
| | |
| | | private lateinit var binding: ActivityMainBinding |
| | | |
| | | private var smsReceiver: SmsReceiver? = null |
| | | |
| | | private lateinit var adapter: MyAdapter |
| | | private lateinit var homeViewModel: HomeViewModel |
| | | |
| | | private val multiplePermissionRequest = |
| | | registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) { permissions -> |
| | |
| | | ) && permissions.getOrDefault(Manifest.permission.READ_SMS, false) -> { |
| | | // 两个权限都获得授权 |
| | | registerSmsReceiver() |
| | | // syncRecentSms() |
| | | // initializeSecurityKeywords() |
| | | } |
| | | |
| | | else -> { |
| | |
| | | binding = ActivityMainBinding.inflate(layoutInflater) |
| | | setContentView(binding.root) |
| | | setupViews() |
| | | // binding.btnLogout.setOnClickListener { |
| | | // logout() |
| | | // } |
| | | // 在此位置初始化 homeViewModel |
| | | // homeViewModel = ViewModelProvider(this).get(HomeViewModel::class.java) |
| | | // |
| | | // val navView: BottomNavigationView = binding.navView |
| | | val navView = binding.navView |
| | | val navController = findNavController(R.id.nav_host_fragment_activity_main) |
| | | |
| | |
| | | registerSmsReceiver() |
| | | syncRecentSms() |
| | | } |
| | | // val recyclerView = findViewById<RecyclerView>(R.id.recyclerView) |
| | | // recyclerView.layoutManager = LinearLayoutManager(this) |
| | | // |
| | | // // 初始化适配器 |
| | | // adapter = MyAdapter() |
| | | // recyclerView.adapter = adapter |
| | | // |
| | | // // 观察 LiveData 数据 |
| | | // homeViewModel.codeList.observe(this) { codeList -> |
| | | // // 如果 codeList 为 null,避免闪退 |
| | | // if (codeList != null) { |
| | | // adapter.submitList(codeList) |
| | | // // 滚动到顶部 |
| | | // recyclerView.scrollToPosition(0) |
| | | // } else { |
| | | // // 如果数据为空,可以显示空列表或其他处理 |
| | | // Toast.makeText(this, "No data available", Toast.LENGTH_SHORT).show() |
| | | // } |
| | | // } |
| | | |
| | | // // 注册广播接收器来监听数据更新 |
| | | // val filter = IntentFilter("com.example.firstapp.DATA_UPDATED") |
| | | // registerReceiver(object : BroadcastReceiver() { |
| | | // override fun onReceive(context: Context, intent: Intent) { |
| | | // // 数据已更新,刷新 LiveData |
| | | // homeViewModel.loadData() |
| | | // } |
| | | // }, filter) |
| | | |
| | | } |
| | | |
| | | private fun registerSmsReceiver() { |
| | |
| | | registerReceiver(smsReceiver, filter) |
| | | } |
| | | |
| | | private fun setupKeywordUpdate() { |
| | | val updateRequest = PeriodicWorkRequestBuilder<KeywordUpdateWorker>( |
| | | 1, TimeUnit.HOURS, // 每小时更新一次 |
| | | 15, TimeUnit.MINUTES // 灵活时间窗口 |
| | | ).build() |
| | | |
| | | WorkManager.getInstance(this).enqueueUniquePeriodicWork( |
| | | "keyword_update", ExistingPeriodicWorkPolicy.REPLACE, updateRequest |
| | | ) |
| | | } |
| | | |
| | | private fun setupViews() { |
| | | // 获取并显示当前登录的手机号 |
| | | val phone = |
| | | getSharedPreferences("user_info", Context.MODE_PRIVATE).getString("phone", "") ?: "" |
| | | |
| | | // binding.apply { |
| | | // tvPhone.text = "当前登录手机号:$phone" |
| | | // } |
| | | getSharedPreferences("user_info", Context.MODE_PRIVATE).getString("phone", "") ?: "" |
| | | } |
| | | |
| | | private fun logout() { |
| | |
| | | val msgId = Core.msg.insert(msg) |
| | | |
| | | // 禁用关键词拦截 |
| | | if (securityKeywordsList.any { it in messageBody }) { |
| | | android.util.Log.d("MainActivity", "历史短信含有禁用关键词,跳过处理") |
| | | continue |
| | | } |
| | | // if (securityKeywordsList.any { it in messageBody }) { |
| | | // android.util.Log.d("MainActivity", "历史短信含有禁用关键词,跳过处理") |
| | | // continue |
| | | // } |
| | | |
| | | // 使用协程处理API调用和数据库操作 |
| | | CoroutineScope(Dispatchers.IO).launch { |
| | |
| | | import com.example.firstapp.utils.PreferencesManager |
| | | import com.google.android.material.bottomsheet.BottomSheetDialog |
| | | import kotlinx.coroutines.launch |
| | | import com.example.firstapp.view.UnderlineTextView |
| | | |
| | | class HomeFragment : Fragment() { |
| | | |
| | | private var _binding: FragmentHomeBinding? = null |
| | | |
| | | // This property is only valid between onCreateView and |
| | | // onDestroyView. |
| | | private val binding get() = _binding!! |
| | | |
| | | private lateinit var homeViewModel: HomeViewModel |
| | |
| | | } |
| | | } |
| | | |
| | | private fun updateTabStyles(selectedTab: TextView) { |
| | | private fun updateTabStyles(selectedTab: UnderlineTextView) { |
| | | binding.apply { |
| | | val tabs = listOf(tabExpress, tabFinance, tabIncome, tabFlight, tabTrain) |
| | | tabs.forEach { tab -> |
| | | // 设置文字颜色为黑色或灰色 |
| | | tab.setTextColor(ContextCompat.getColor(requireContext(), |
| | | if (tab == selectedTab) R.color.tab_selected else R.color.gray)) |
| | | if (tab == selectedTab) android.R.color.black else R.color.gray)) |
| | | // 设置文字大小 |
| | | tab.textSize = if (tab == selectedTab) 16f else 14f |
| | | // 设置下划线 |
| | | tab.setUnderlineVisible(tab == selectedTab) |
| | | } |
| | | } |
| | | } |
对比新文件 |
| | |
| | | package com.example.firstapp.view |
| | | |
| | | import android.content.Context |
| | | import android.graphics.Canvas |
| | | import android.graphics.Paint |
| | | import android.util.AttributeSet |
| | | import androidx.appcompat.widget.AppCompatTextView |
| | | import androidx.core.content.ContextCompat |
| | | |
| | | class UnderlineTextView @JvmOverloads constructor( |
| | | context: Context, |
| | | attrs: AttributeSet? = null, |
| | | defStyleAttr: Int = 0 |
| | | ) : AppCompatTextView(context, attrs, defStyleAttr) { |
| | | |
| | | private val underlinePaint = Paint().apply { |
| | | color = ContextCompat.getColor(context, android.R.color.black) |
| | | strokeWidth = context.resources.displayMetrics.density * 2 // 2dp |
| | | } |
| | | |
| | | private var showUnderline = false |
| | | |
| | | fun setUnderlineVisible(visible: Boolean) { |
| | | showUnderline = visible |
| | | invalidate() |
| | | } |
| | | |
| | | override fun onDraw(canvas: Canvas) { |
| | | super.onDraw(canvas) |
| | | |
| | | if (showUnderline) { |
| | | val textWidth = paint.measureText(text.toString()) |
| | | val startX = (width - textWidth) / 2 |
| | | canvas.drawLine( |
| | | startX, |
| | | height - underlinePaint.strokeWidth, |
| | | startX + textWidth, |
| | | height - underlinePaint.strokeWidth, |
| | | underlinePaint |
| | | ) |
| | | } |
| | | } |
| | | } |
| | |
| | | <vector xmlns:android="http://schemas.android.com/apk/res/android" |
| | | android:width="217.6dp" |
| | | android:height="200dp" |
| | | android:viewportWidth="1114" |
| | | android:width="48dp" |
| | | android:height="48dp" |
| | | android:viewportWidth="1024" |
| | | android:viewportHeight="1024"> |
| | | <path |
| | | android:pathData="M1081,394.7l-162.5,-278.6c-41.8,-72 -118.4,-116.1 -199.7,-116.1L393.7,-0c-81.3,0 -160.2,44.1 -199.7,116.1l-162.5,278.6c-41.8,72 -41.8,162.5 0,234.5l162.5,278.6c41.8,72 118.4,116.1 199.7,116.1h325.1c81.3,0 160.2,-44.1 199.7,-116.1l162.5,-278.6c41.8,-74.3 41.8,-162.5 0,-234.5zM1002,580.5l-162.5,278.6c-25.5,41.8 -72,69.7 -120.7,69.7L393.7,928.8c-48.8,0 -95.2,-25.5 -120.7,-69.7l-162.5,-278.6c-25.5,-44.1 -25.5,-97.5 0,-139.3l162.5,-278.6c25.5,-41.8 72,-69.7 120.7,-69.7h325.1c48.8,0 95.2,25.5 120.7,69.7l162.5,278.6c25.5,41.8 25.5,97.5 0,139.3z" |
| | | android:fillColor="#707070"/> |
| | | <path |
| | | android:pathData="M556.2,301.9c-116.1,0 -209,92.9 -209,209s92.9,209 209,209 209,-92.9 209,-209 -92.9,-209 -209,-209zM556.2,626.9c-65,0 -116.1,-51.1 -116.1,-116.1s51.1,-116.1 116.1,-116.1 116.1,51.1 116.1,116.1 -51.1,116.1 -116.1,116.1z" |
| | | android:fillColor="#707070"/> |
| | | android:fillColor="#FF000000" |
| | | android:pathData="M737,647c-5.6,-5.6 -11.3,-11.3 -16.9,-11.3 -16.9,0 -28.1,11.3 -28.1,28.1 0,11.3 5.6,16.9 11.3,22.5C770.8,737 815.8,815.8 821.4,905.8h50.6c-5.6,-106.9 -56.3,-196.9 -135,-258.8M523.3,551.4c-101.3,0 -185.6,-84.4 -185.6,-185.6C332,258.9 416.4,174.5 523.3,174.5 624.5,174.5 708.9,258.9 708.9,360.1c0,106.9 -84.4,191.3 -185.6,191.3zM765.1,365.8C765.1,230.8 658.3,118.3 517.6,118.3c-129.4,0 -241.9,106.9 -241.9,247.5 0,90 50.6,168.8 123.8,213.8 -140.6,39.4 -241.9,168.8 -247.5,326.3H202.6c5.6,-163.1 140.6,-292.5 303.8,-298.1H523.3c135,0 241.9,-106.9 241.9,-241.9z"/> |
| | | </vector> |
对比新文件 |
| | |
| | | <?xml version="1.0" encoding="utf-8"?> |
| | | <layer-list xmlns:android="http://schemas.android.com/apk/res/android"> |
| | | <item android:gravity="bottom"> |
| | | <shape android:shape="rectangle"> |
| | | <size android:height="2dp" /> |
| | | <solid android:color="@android:color/black" /> |
| | | </shape> |
| | | </item> |
| | | </layer-list> |
| | |
| | | |
| | | |
| | | <!-- 快递/财务切换区域 --> |
| | | <LinearLayout |
| | | <FrameLayout |
| | | android:layout_width="match_parent" |
| | | android:layout_height="wrap_content" |
| | | android:layout_marginBottom="8dp" |
| | | android:layout_marginTop="8dp" |
| | | android:layout_marginHorizontal="8dp" |
| | | android:orientation="horizontal"> |
| | | android:layout_marginHorizontal="8dp"> |
| | | |
| | | <TextView |
| | | android:id="@+id/tabExpress" |
| | | android:layout_width="0dp" |
| | | <LinearLayout |
| | | android:layout_width="match_parent" |
| | | android:layout_height="wrap_content" |
| | | android:layout_weight="1" |
| | | android:gravity="center" |
| | | android:padding="6dp" |
| | | android:text="快递" |
| | | android:textSize="14sp" |
| | | android:textStyle="bold" /> |
| | | android:layout_marginEnd="30dp" |
| | | android:orientation="horizontal"> |
| | | |
| | | <TextView |
| | | android:id="@+id/tabFinance" |
| | | android:layout_width="0dp" |
| | | android:layout_height="wrap_content" |
| | | android:layout_weight="1" |
| | | android:gravity="center" |
| | | android:padding="6dp" |
| | | android:text="还款" |
| | | android:textSize="14sp" /> |
| | | <com.example.firstapp.view.UnderlineTextView |
| | | android:id="@+id/tabExpress" |
| | | android:layout_width="0dp" |
| | | android:layout_height="wrap_content" |
| | | android:layout_weight="1" |
| | | android:gravity="center" |
| | | android:padding="6dp" |
| | | android:text="快递" |
| | | android:textSize="14sp" |
| | | android:textStyle="bold" /> |
| | | |
| | | <TextView |
| | | android:id="@+id/tabIncome" |
| | | android:layout_width="0dp" |
| | | android:layout_height="wrap_content" |
| | | android:layout_weight="1" |
| | | android:gravity="center" |
| | | android:padding="6dp" |
| | | android:text="收入" |
| | | android:textSize="14sp" /> |
| | | <com.example.firstapp.view.UnderlineTextView |
| | | android:id="@+id/tabFinance" |
| | | android:layout_width="0dp" |
| | | android:layout_height="wrap_content" |
| | | android:layout_weight="1" |
| | | android:gravity="center" |
| | | android:padding="6dp" |
| | | android:text="还款" |
| | | android:textSize="14sp" /> |
| | | |
| | | <TextView |
| | | android:id="@+id/tabFlight" |
| | | android:layout_width="0dp" |
| | | android:layout_height="wrap_content" |
| | | android:layout_weight="1" |
| | | android:gravity="center" |
| | | android:padding="6dp" |
| | | android:text="航班" |
| | | android:textSize="14sp" /> |
| | | <com.example.firstapp.view.UnderlineTextView |
| | | android:id="@+id/tabIncome" |
| | | android:layout_width="0dp" |
| | | android:layout_height="wrap_content" |
| | | android:layout_weight="1" |
| | | android:gravity="center" |
| | | android:padding="6dp" |
| | | android:text="收入" |
| | | android:textSize="14sp" /> |
| | | |
| | | <TextView |
| | | android:id="@+id/tabTrain" |
| | | android:layout_width="0dp" |
| | | android:layout_height="wrap_content" |
| | | android:layout_weight="1" |
| | | android:gravity="center" |
| | | android:padding="6dp" |
| | | android:text="火车票" |
| | | android:textSize="14sp" /> |
| | | <com.example.firstapp.view.UnderlineTextView |
| | | android:id="@+id/tabFlight" |
| | | android:layout_width="0dp" |
| | | android:layout_height="wrap_content" |
| | | android:layout_weight="1" |
| | | android:gravity="center" |
| | | android:padding="6dp" |
| | | android:text="航班" |
| | | android:textSize="14sp" /> |
| | | |
| | | <com.example.firstapp.view.UnderlineTextView |
| | | android:id="@+id/tabTrain" |
| | | android:layout_width="0dp" |
| | | android:layout_height="wrap_content" |
| | | android:layout_weight="1" |
| | | android:gravity="center" |
| | | android:padding="6dp" |
| | | android:text="火车票" |
| | | android:textSize="14sp" /> |
| | | |
| | | </LinearLayout> |
| | | |
| | | <ImageButton |
| | | android:id="@+id/categoryButton" |
| | | android:layout_width="28dp" |
| | | android:layout_height="28dp" |
| | | android:layout_gravity="center_vertical" |
| | | android:layout_marginStart="2dp" |
| | | android:layout_marginEnd="2dp" |
| | | android:layout_gravity="center_vertical|end" |
| | | android:background="?attr/selectableItemBackgroundBorderless" |
| | | android:contentDescription="分类设置" |
| | | android:padding="4dp" |
| | | android:scaleType="fitCenter" |
| | | android:src="@drawable/home_add" /> |
| | | |
| | | </LinearLayout> |
| | | </FrameLayout> |
| | | |
| | | <!-- 内容区域 --> |
| | | <FrameLayout |