11、我的	切换头像
点击切换头像没有显示允许存储权限的窗口,华为的手机目前有
49、首页 实时刷新
点击全部取件或其他分类后,回到上一层,内容没有刷新(5个分类)
52、数据统计 数据统计
1.周月年的柱状图统计逻辑需要修改为只统计快递类的数据
2.按年的图形统计,右下方加上图示说明
53、首页 首页登录
点击用户协议、隐私政策无反应
已修改8个文件
303 ■■■■ 文件已修改
app/src/main/AndroidManifest.xml 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/java/com/example/firstapp/activity/LoginActivity.kt 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/java/com/example/firstapp/database/dao/CodeDao.kt 14 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/java/com/example/firstapp/ui/dashboard/DashboardFragment.kt 151 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/java/com/example/firstapp/ui/home/HomeFragment.kt 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/java/com/example/firstapp/ui/home/HomeViewModel.kt 30 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/java/com/example/firstapp/ui/profile/EditProfileActivity.kt 67 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/layout/layout_week_stats.xml 26 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/AndroidManifest.xml
@@ -72,6 +72,9 @@
    <uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" /> <!-- 允许语言识别。 -->
    <uses-permission android:name="android.permission.RECORD_AUDIO" />
    <!-- Android 13 及以上 -->
    <uses-permission android:name="android.permission.READ_MEDIA_IMAGES"/>
    <application
        android:name=".App"
        android:allowBackup="true"
app/src/main/java/com/example/firstapp/activity/LoginActivity.kt
@@ -55,10 +55,12 @@
        binding.tvUserAgreement.setOnClickListener {
            // 打开用户协议
            startContentActivity("用户协议", "服务使用协议")
        }
        binding.tvPrivacyPolicy.setOnClickListener {
            // 打开隐私政策
            startContentActivity("隐私协议", "隐私保护政策")
        }
    }
app/src/main/java/com/example/firstapp/database/dao/CodeDao.kt
@@ -202,7 +202,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 and code.category='快递'
        GROUP BY dates.date
        ORDER BY dates.date
    """)
@@ -237,12 +237,12 @@
        )
        SELECT 
            strftime('%m', month_start) as date,
            COUNT(code.id) as count,
            COUNT(c.id) as count,
            strftime('%Y-%m-%d', month_start) as weekStart
        FROM months
        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
        FROM months m
        LEFT JOIN code c ON strftime('%Y-%m', c.createTime) = strftime('%Y-%m', m.month_start)  and c.category='快递'
        GROUP BY m.month_start
        ORDER BY m.month_start ASC
    """)
    fun getYearMonthlyStats(date: Long): Flow<List<DailyStat>>
@@ -335,7 +335,7 @@
        COUNT(c.id) AS count,
        '' AS weekStart
    FROM dates d
    LEFT JOIN code c ON date(c.createTime) = d.date_value
    LEFT JOIN code c ON date(c.createTime) = d.date_value and c.category='快递'
    GROUP BY d.date_value
    ORDER BY d.date_value ASC
""")
app/src/main/java/com/example/firstapp/ui/dashboard/DashboardFragment.kt
@@ -21,7 +21,9 @@
import java.util.*
import java.text.SimpleDateFormat
import android.graphics.Color
import android.view.Gravity
import android.widget.GridLayout
import android.widget.LinearLayout
import android.widget.Toast
import androidx.cardview.widget.CardView
import androidx.lifecycle.lifecycleScope
@@ -782,6 +784,153 @@
                removeAllViews()
                
                val gridLayout = GridLayout(context).apply {
                    rowCount = 8 // 1行月份 + 7行星期
                    columnCount = 53 // 1列星期 + 52列周数
                }
                // 添加月份标签
                val months = arrayOf("1月", "2月", "3月", "4月", "5月", "6月",
                    "7月", "8月", "9月", "10月", "11月", "12月")
                months.forEachIndexed { index, month ->
                    val label = TextView(context).apply {
                        text = month
                        textSize = 10f
                        setPadding(0, 0, 8, 4)
                        val weekPosition = (index * 4.3).toInt() // 估算月份起始位置
                        layoutParams = GridLayout.LayoutParams().apply {
                            columnSpec = GridLayout.spec(weekPosition + 1)
                            rowSpec = GridLayout.spec(0)
                        }
                    }
                    gridLayout.addView(label)
                }
                // 添加星期标签
                val dayLabels = arrayOf("周一", "周二", "周三", "周四", "周五", "周六", "周日")
                dayLabels.forEachIndexed { index, label ->
                    val textView = TextView(context).apply {
                        text = label
                        textSize = 10f
                        setPadding(4, 0, 8, 0)
                        layoutParams = GridLayout.LayoutParams().apply {
                            columnSpec = GridLayout.spec(0)
                            rowSpec = GridLayout.spec(index + 1)
                        }
                    }
                    gridLayout.addView(textView)
                }
                // 添加热力图单元格
                for (day in 0..6) {
                    for (week in 0..51) {
                        val count = heatmapMatrix[day][week]
                        val cell = View(context).apply {
                            layoutParams = GridLayout.LayoutParams().apply {
                                width = resources.getDimensionPixelSize(R.dimen.heatmap_cell_size)
                                height = resources.getDimensionPixelSize(R.dimen.heatmap_cell_size)
                                columnSpec = GridLayout.spec(week + 1)
                                rowSpec = GridLayout.spec(day + 1)
                                setMargins(1, 1, 1, 1)
                            }
                            setBackgroundColor(getHeatmapColor(count))
                        }
                        gridLayout.addView(cell)
                    }
                }
                // 创建图例(Legend)
                val legendLayout = LinearLayout(context).apply {
                    orientation = LinearLayout.HORIZONTAL
                    gravity = Gravity.END or Gravity.CENTER_VERTICAL // 靠右对齐
                    setPadding(8, 16, 8, 0)
                    layoutParams = LinearLayout.LayoutParams(
                        ViewGroup.LayoutParams.MATCH_PARENT,
                        ViewGroup.LayoutParams.WRAP_CONTENT
                    )
                }
                // 左侧“少”标签
                val labelLow = TextView(context).apply {
                    text = "少"
                    textSize = 10f
                    setPadding(0, 0, 8, 0)
                }
                legendLayout.addView(labelLow)
                // 渐变色块 + 数值
                val legendLevels = listOf(0, 5, 10, 15, 20)
                legendLevels.forEach { level ->
                    val colorBox = View(context).apply {
                        setBackgroundColor(getHeatmapColor(level))
                        val size = resources.getDimensionPixelSize(R.dimen.heatmap_cell_size)
                        layoutParams = LinearLayout.LayoutParams(size, size).apply {
                            marginEnd = 4
                        }
                    }
                    val label = TextView(context).apply {
//                        text = "$level"
                        textSize = 10f
                        setPadding(0, 0, 8, 0)
                    }
                    legendLayout.addView(colorBox)
                    legendLayout.addView(label)
                }
                // 右侧“多”标签
                val labelHigh = TextView(context).apply {
                    text = "多"
                    textSize = 10f
                    setPadding(8, 0, 0, 0)
                }
                legendLayout.addView(labelHigh)
                // 垂直组合 gridLayout + legendLayout
                val container = LinearLayout(context).apply {
                    orientation = LinearLayout.VERTICAL
                    addView(gridLayout)
                    addView(legendLayout)
                }
                addView(container)
            }
        }
    }
    private fun getHeatmapColor(count: Int): Int {
        return when {
            count == 0 -> Color.parseColor("#EBECF1") // 最淡
            count < 5 -> Color.parseColor("#ACE7B1")
            count < 10 -> Color.parseColor("#68C16F")
            count < 15 -> Color.parseColor("#529F57")
            else -> Color.parseColor("#356D40") // 最深
        }
    }
    private fun updateHeatmapData_bak() {
        viewModel.getYearlyHeatmap(currentDate.timeInMillis).observe(viewLifecycleOwner) { stats ->
            if (stats.isEmpty()) return@observe
            // 创建52周x7天的数据矩阵
            val heatmapMatrix = Array(7) { IntArray(52) }
            // 填充数据
            stats.forEach { stat ->
                val week = stat.weekOfYear - 1 // 0-51
                val dayOfWeek = stat.dayOfWeek - 1 // 0-6
                if (week in 0..51 && dayOfWeek in 0..6) {
                    heatmapMatrix[dayOfWeek][week] = stat.count
                }
            }
            // 更新UI
            binding.layoutWeekStats.heatmapYearly.apply {
                removeAllViews()
                val gridLayout = GridLayout(context).apply {
                    rowCount = 8 // 增加一行用于显示月份
                    columnCount = 53 // 增加一列用于显示星期标签
                }
@@ -843,7 +992,7 @@
        }
    }
    private fun getHeatmapColor(count: Int): Int {
    private fun getHeatmapColor_bak(count: Int): Int {
        return when {
            count == 0 -> Color.parseColor("#EBEDF0")
            count == 1 -> Color.parseColor("#9BE9A8")
app/src/main/java/com/example/firstapp/ui/home/HomeFragment.kt
@@ -124,6 +124,7 @@
        }
    }
    private fun setupAdapters() {
        binding.expressRecycler.apply {
            layoutManager = LinearLayoutManager(context)
@@ -438,9 +439,16 @@
        )
        // 加载数据
        homeViewModel.loadExpressData()
        homeViewModel.loadAllCategoryData()
//        homeViewModel.loadExpressData()
//        homeViewModel.loadFinanceData()
//        homeViewModel.loadIncomeData()
//        homeViewModel.loadFlightData()
//        homeViewModel.loadTrainData()
        // 检查未读提醒数量
        homeViewModel.checkUnreadReminders()
    }
    override fun onPause() {
app/src/main/java/com/example/firstapp/ui/home/HomeViewModel.kt
@@ -192,17 +192,37 @@
        loadDataByType("火车票")
    }
    private fun loadCategories() {
        viewModelScope.launch {
            try {
                // 默认完整分类列表
                val fullCategories = listOf(
    fun loadAllCategoryData() {
        getFullCategories().forEach { category ->
            loadDataByType(category.name)
        }
    }
    fun getFullCategories(): List<CategoryConfig> {
        return listOf(
                    CategoryConfig(1, "快递", 0, true),
                    CategoryConfig(2, "还款", 1, true),
                    CategoryConfig(3, "收入", 2, true),
                    CategoryConfig(4, "航班", 3, true),
                    CategoryConfig(5, "火车票", 4, true)
                )
    }
    private fun loadCategories() {
        viewModelScope.launch {
            try {
                // 默认完整分类列表
//                val fullCategories = listOf(
//                    CategoryConfig(1, "快递", 0, true),
//                    CategoryConfig(2, "还款", 1, true),
//                    CategoryConfig(3, "收入", 2, true),
//                    CategoryConfig(4, "航班", 3, true),
//                    CategoryConfig(5, "火车票", 4, true)
//                )
                val fullCategories=getFullCategories()
                
                // 基础分类(非会员可见)
                val basicCategories = listOf(
app/src/main/java/com/example/firstapp/ui/profile/EditProfileActivity.kt
@@ -36,15 +36,41 @@
    private var selectedImageUri: Uri? = null
    private var loadingDialog: AlertDialog? = null
    private val pickImage = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
        if (result.resultCode == Activity.RESULT_OK) {
            result.data?.data?.let { uri ->
//    private val pickImage = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
//        if (result.resultCode == Activity.RESULT_OK) {
//            result.data?.data?.let { uri ->
//                selectedImageUri = uri
//                Glide.with(this)
//                    .load(uri)
//                    .circleCrop()
//                    .into(binding.ivAvatar)
//            }
//        }
//    }
    private val pickImageLauncher = registerForActivityResult(
        ActivityResultContracts.GetContent()
    ) { uri: Uri? ->
        uri?.let {
                selectedImageUri = uri
            // 这里直接处理选中的头像
                Glide.with(this)
                    .load(uri)
                    .circleCrop()
                    .into(binding.ivAvatar)
                .load(it)
                .into(binding.ivAvatar) // 替换成你的 ImageView id
            // 如果需要上传可以用 contentResolver.openInputStream(uri)
            }
    }
    // 👇 就放在这里
    private val permissionLauncher = registerForActivityResult(
        ActivityResultContracts.RequestPermission()
    ) { isGranted: Boolean ->
        if (isGranted) {
            openGallery()
        } else {
            Toast.makeText(this, "需要权限才能选择头像", Toast.LENGTH_SHORT).show()
        }
    }
@@ -78,13 +104,29 @@
        }
        binding.ivAvatar.setOnClickListener {
            checkAndRequestPermission()
//            checkAndRequestPermission()
            checkStoragePermission()
        }
        binding.btnSaveBottom.setOnClickListener {
            saveAndFinish()
        }
    }
    fun checkStoragePermission() {
        val permission = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
            Manifest.permission.READ_MEDIA_IMAGES
        } else {
            Manifest.permission.READ_EXTERNAL_STORAGE
        }
        if (ContextCompat.checkSelfPermission(this, permission) != PackageManager.PERMISSION_GRANTED) {
            permissionLauncher.launch(permission)
        } else {
            openGallery()
        }
    }
    private fun saveAndFinish() {
        lifecycleScope.launch {
@@ -194,9 +236,16 @@
        }
    }
    private fun openGallery() {
    private fun openGallery_bak() {
        val intent = Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI)
        pickImage.launch(intent)
//        pickImage.launch(intent)
    }
    private fun openGallery() {
        pickImageLauncher.launch("image/*")
//        val intent = Intent(Intent.ACTION_PICK)
//        intent.type = "image/*"
//        startActivityForResult(intent, 1001)
    }
    private fun showLoading() {
app/src/main/res/layout/layout_week_stats.xml
@@ -43,20 +43,22 @@
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginHorizontal="16dp"
        android:layout_marginBottom="16dp">
        android:layout_marginBottom="16dp"
        android:padding="16dp"
        >
        <HorizontalScrollView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:padding="16dp">
<!--        <HorizontalScrollView-->
<!--            android:layout_width="match_parent"-->
<!--            android:layout_height="wrap_content"-->
<!--            android:padding="16dp">-->
            <LinearLayout
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:orientation="horizontal">
                <!-- 热力图将在这里动态添加 -->
            </LinearLayout>
        </HorizontalScrollView>
<!--            <LinearLayout-->
<!--                android:layout_width="wrap_content"-->
<!--                android:layout_height="wrap_content"-->
<!--                android:orientation="horizontal">-->
<!--                &lt;!&ndash; 热力图将在这里动态添加 &ndash;&gt;-->
<!--            </LinearLayout>-->
<!--        </HorizontalScrollView>-->
    </androidx.cardview.widget.CardView>