feat: consent screen, reminder system, medical metrics, sex field, design tokens, code quality

A. Reminder: ReminderService (AlarmManager chain), ReminderSettingsView,
   bell icon in Piezo top bar, reschedule on catheterization
B. Consent: ConsentView (3 mandatory items), onboarding→consent→registration flow,
   persistent consent storage, sex field in User + Registration Step 2
C. Diary: 24hUV, NUV, NPI, max capacity, day/night frequency metrics card
D. Design: DesignTokens.kt, 48dp touch targets, DonutChart semantics, pressScale modifier
E. Quality: BuildConfig.DEBUG log wrapping, DateFormatter caching, background I/O for diary store

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-07 14:26:02 +09:00
parent 1d5c6f15f8
commit 0e13f2bbb6
19 changed files with 965 additions and 68 deletions
@@ -4,6 +4,9 @@ import android.content.Context
import android.content.SharedPreferences
import com.google.gson.Gson
import com.google.gson.reflect.TypeToken
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import java.text.SimpleDateFormat
import java.util.*
@@ -17,11 +20,16 @@ data class VoidingRecord(
val method: String = "manual", // "manual", "voice", "camera"
val timestamp: Long = System.currentTimeMillis()
) {
companion object {
private val timeFormat = SimpleDateFormat("HH:mm", Locale.getDefault())
private val dateFormat = SimpleDateFormat("yyyy-MM-dd", Locale.getDefault())
}
val timeString: String
get() = SimpleDateFormat("HH:mm", Locale.getDefault()).format(Date(timestamp))
get() = timeFormat.format(Date(timestamp))
val dateString: String
get() = SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()).format(Date(timestamp))
get() = dateFormat.format(Date(timestamp))
val displayText: String
get() = when {
@@ -35,6 +43,7 @@ data class VoidingRecord(
/**
* 로컬 배뇨 기록 저장소 (SharedPreferences + JSON)
*/
@OptIn(kotlinx.coroutines.DelicateCoroutinesApi::class)
object VoidingRecordStore {
private const val PREFS_NAME = "voiding_records"
private const val KEY_RECORDS = "records"
@@ -61,11 +70,15 @@ object VoidingRecordStore {
}
fun clear(context: Context) {
prefs(context).edit().remove(KEY_RECORDS).apply()
GlobalScope.launch(Dispatchers.IO) {
prefs(context).edit().remove(KEY_RECORDS).apply()
}
}
private fun save(context: Context, records: List<VoidingRecord>) {
prefs(context).edit().putString(KEY_RECORDS, gson.toJson(records)).apply()
GlobalScope.launch(Dispatchers.IO) {
prefs(context).edit().putString(KEY_RECORDS, gson.toJson(records)).apply()
}
}
fun getToday(context: Context): List<VoidingRecord> {