From f67cf3b81a00f732ca743431258ae6b78f5f40ab Mon Sep 17 00:00:00 2001
From: tj <1378534974@qq.com>
Date: 星期四, 17 四月 2025 15:05:28 +0800
Subject: [PATCH] 11、我的	切换头像  点击切换头像没有显示允许存储权限的窗口,华为的手机目前有 49、首页	实时刷新  点击全部取件或其他分类后,回到上一层,内容没有刷新(5个分类) 52、数据统计	数据统计  1.周月年的柱状图统计逻辑需要修改为只统计快递类的数据  2.按年的图形统计,右下方加上图示说明 53、首页	首页登录  点击用户协议、隐私政策无反应

---
 app/src/main/java/com/example/firstapp/ui/home/HomeFragment.kt           |   10 +
 app/src/main/java/com/example/firstapp/ui/profile/EditProfileActivity.kt |   73 ++++++++++--
 app/src/main/java/com/example/firstapp/ui/dashboard/DashboardFragment.kt |  151 +++++++++++++++++++++++++
 app/src/main/AndroidManifest.xml                                         |    3 
 app/src/main/java/com/example/firstapp/database/dao/CodeDao.kt           |   14 +-
 app/src/main/res/layout/layout_week_stats.xml                            |   26 ++--
 app/src/main/java/com/example/firstapp/activity/LoginActivity.kt         |    2 
 app/src/main/java/com/example/firstapp/ui/home/HomeViewModel.kt          |   34 ++++-
 8 files changed, 273 insertions(+), 40 deletions(-)

diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 17c8460..4004bcd 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/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"
diff --git a/app/src/main/java/com/example/firstapp/activity/LoginActivity.kt b/app/src/main/java/com/example/firstapp/activity/LoginActivity.kt
index 36ac653..fcb8a6b 100644
--- a/app/src/main/java/com/example/firstapp/activity/LoginActivity.kt
+++ b/app/src/main/java/com/example/firstapp/activity/LoginActivity.kt
@@ -55,10 +55,12 @@
 
         binding.tvUserAgreement.setOnClickListener {
             // 打开用户协议
+            startContentActivity("用户协议", "服务使用协议")
         }
 
         binding.tvPrivacyPolicy.setOnClickListener {
             // 打开隐私政策
+            startContentActivity("隐私协议", "隐私保护政策")
         }
     }
 
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 c3b0d52..e63ba6a 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
@@ -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
 """)
diff --git a/app/src/main/java/com/example/firstapp/ui/dashboard/DashboardFragment.kt b/app/src/main/java/com/example/firstapp/ui/dashboard/DashboardFragment.kt
index 0c5316d..b689d58 100644
--- a/app/src/main/java/com/example/firstapp/ui/dashboard/DashboardFragment.kt
+++ b/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
@@ -767,6 +769,153 @@
 
             // 创建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 // 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 ->
@@ -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")
diff --git a/app/src/main/java/com/example/firstapp/ui/home/HomeFragment.kt b/app/src/main/java/com/example/firstapp/ui/home/HomeFragment.kt
index 03440a8..d86a5c4 100644
--- a/app/src/main/java/com/example/firstapp/ui/home/HomeFragment.kt
+++ b/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() {
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 b367f26..e1357b3 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
@@ -192,17 +192,37 @@
         loadDataByType("火车票")
     }
 
+
+    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 = 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(
diff --git a/app/src/main/java/com/example/firstapp/ui/profile/EditProfileActivity.kt b/app/src/main/java/com/example/firstapp/ui/profile/EditProfileActivity.kt
index a7f4af1..608eef6 100644
--- a/app/src/main/java/com/example/firstapp/ui/profile/EditProfileActivity.kt
+++ b/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 ->
-                selectedImageUri = uri
-                Glide.with(this)
-                    .load(uri)
-                    .circleCrop()
-                    .into(binding.ivAvatar)
-            }
+//    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(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() {
diff --git a/app/src/main/res/layout/layout_week_stats.xml b/app/src/main/res/layout/layout_week_stats.xml
index 2499968..a328499 100644
--- a/app/src/main/res/layout/layout_week_stats.xml
+++ b/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>
 
 

--
Gitblit v1.9.3