Initial commit: Implement base health and cycle tracking application.

- Set up Android project structure with Gradle 8.7, Kotlin 2.0, and Hilt.
- Implement Room database with entities for Profiles, Day Logs, Conditions, Cycle Records, and Intimacy Logs.
- Integrate Jetpack Compose for the UI layer including Navigation and Material3.
- Develop a cycle prediction engine to calculate menstruation, fertile windows, and ovulation based on user history.
- Implement core screens:
    - **Onboarding:** Initial profile setup for female/male users.
    - **Calendar:** Monthly view showing cycle phases, logged symptoms, and intimacy records.
    - **Day Detail:** Detailed logging for symptoms (with ratings), period status, notes, and intimacy encounters.
    - **Cycle Insights:** Visualization of cycle phases and historical cycle length trends.
    - **Health Trends:** Frequency and recurrence analysis of logged health conditions over various time ranges.
    - **Settings:** Profile management, data clearing, and app theme configuration.
- Add DataStore for managing user preferences such as active profile and onboarding status.

Signed-off-by: whitlocktech <whitlocktech@gmail.com>
This commit is contained in:
2026-05-22 17:21:30 -05:00
commit bba09dc131
138 changed files with 8150 additions and 0 deletions

View File

@@ -0,0 +1,78 @@
package com.hsdiary.ui.components
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
data class ProfileSwitchItem(
val id: Long,
val name: String,
val avatarColor: Color,
val isActive: Boolean
)
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun ProfileSwitchSheet(
profiles: List<ProfileSwitchItem>,
onProfileSelected: (Long) -> Unit,
onDismiss: () -> Unit
) {
var confirmTarget by remember { mutableStateOf<ProfileSwitchItem?>(null) }
ModalBottomSheet(onDismissRequest = onDismiss) {
Column(modifier = Modifier.padding(horizontal = 24.dp).padding(bottom = 32.dp)) {
Text(
text = "Switch Profile",
style = MaterialTheme.typography.titleMedium,
modifier = Modifier.padding(bottom = 16.dp)
)
profiles.forEach { profile ->
Row(
modifier = Modifier
.fillMaxWidth()
.clickable {
if (!profile.isActive) confirmTarget = profile
else onDismiss()
}
.padding(vertical = 12.dp),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(16.dp)
) {
AvatarDot(name = profile.name, avatarColor = profile.avatarColor)
Text(
text = profile.name,
style = MaterialTheme.typography.bodyLarge,
modifier = Modifier.weight(1f)
)
if (profile.isActive) {
Badge { Text("Active") }
}
}
}
}
}
confirmTarget?.let { target ->
AlertDialog(
onDismissRequest = { confirmTarget = null },
title = { Text("Switch Profile") },
text = { Text("Switch to ${target.name}?") },
confirmButton = {
TextButton(onClick = {
onProfileSelected(target.id)
confirmTarget = null
onDismiss()
}) { Text("Switch") }
},
dismissButton = {
TextButton(onClick = { confirmTarget = null }) { Text("Cancel") }
}
)
}
}