diff --git a/app/src/main/java/com/cornellappdev/score/MainActivity.kt b/app/src/main/java/com/cornellappdev/score/MainActivity.kt index 512793e..aaf0da5 100644 --- a/app/src/main/java/com/cornellappdev/score/MainActivity.kt +++ b/app/src/main/java/com/cornellappdev/score/MainActivity.kt @@ -5,11 +5,12 @@ import androidx.activity.compose.setContent import androidx.activity.enableEdgeToEdge import androidx.appcompat.app.AppCompatActivity import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.mutableIntStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import com.cornellappdev.score.nav.root.RootNavigation +import com.cornellappdev.score.screen.OnboardingProfileSelectScreen import com.cornellappdev.score.screen.OnboardingScreen import com.google.firebase.Firebase import com.google.firebase.analytics.FirebaseAnalytics @@ -18,6 +19,7 @@ import dagger.hilt.android.AndroidEntryPoint private const val APP_PREFERENCES = "score_app_preferences" private const val KEY_ONBOARDING_COMPLETED = "onboarding_completed" +private const val KEY_ONBOARDING_PROFILE_PICTURE = "onboarding_profile_picture" private lateinit var firebaseAnalytics: FirebaseAnalytics @@ -31,8 +33,8 @@ class MainActivity : AppCompatActivity() { val onboardingCompleted = preferences.getBoolean(KEY_ONBOARDING_COMPLETED, false) setContent { - var showOnboarding by rememberSaveable { - mutableStateOf(!onboardingCompleted) + var onboardingStep by rememberSaveable { + mutableIntStateOf(if (onboardingCompleted) 2 else 0) } val completeOnboarding = remember { @@ -40,18 +42,25 @@ class MainActivity : AppCompatActivity() { preferences.edit() .putBoolean(KEY_ONBOARDING_COMPLETED, true) .apply() - showOnboarding = false + onboardingStep = 2 } } - if (showOnboarding) { - OnboardingScreen( - onSignUp = completeOnboarding, + when (onboardingStep) { + 0 -> OnboardingScreen( + onSignUp = { onboardingStep = 1 }, onLogIn = completeOnboarding, onLater = completeOnboarding ) - } else { - RootNavigation() + 1 -> OnboardingProfileSelectScreen( + onNext = { selectedPicture -> + preferences.edit() + .putString(KEY_ONBOARDING_PROFILE_PICTURE, selectedPicture.name) + .apply() + completeOnboarding() + } + ) + else -> RootNavigation() } } diff --git a/app/src/main/java/com/cornellappdev/score/screen/EditProfileScreen.kt b/app/src/main/java/com/cornellappdev/score/screen/EditProfileScreen.kt index f81b4b0..4c93ff0 100644 --- a/app/src/main/java/com/cornellappdev/score/screen/EditProfileScreen.kt +++ b/app/src/main/java/com/cornellappdev/score/screen/EditProfileScreen.kt @@ -4,23 +4,14 @@ import androidx.annotation.DrawableRes import androidx.annotation.StringRes import androidx.compose.foundation.Image import androidx.compose.foundation.background -import androidx.compose.foundation.border -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.aspectRatio import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size -import androidx.compose.foundation.layout.widthIn import androidx.compose.foundation.rememberScrollState -import androidx.compose.foundation.selection.selectable -import androidx.compose.foundation.selection.selectableGroup -import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.verticalScroll import androidx.compose.material3.Text import androidx.compose.runtime.Composable @@ -30,11 +21,9 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource -import androidx.compose.ui.semantics.Role import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import com.cornellappdev.score.R @@ -89,31 +78,10 @@ fun EditProfileScreen( modifier = Modifier.padding(horizontal = 24.dp) ) Spacer(Modifier.height(24.dp)) - Row( - modifier = Modifier.widthIn(max = 393.dp).fillMaxWidth() - .padding(horizontal = 24.dp).selectableGroup(), - horizontalArrangement = Arrangement.spacedBy(20.dp) - ) { - ProfilePicture.entries.forEach { picture -> - val selected = picture == selectedPicture - Box( - modifier = Modifier.weight(1f).aspectRatio(1f).clip(CircleShape) - .selectable(selected = selected, role = Role.RadioButton, - onClick = { onPictureSelected(picture) }), - contentAlignment = Alignment.Center - ) { - Image( - painter = painterResource(picture.drawable), - contentDescription = stringResource(picture.label), - modifier = Modifier.fillMaxSize() - ) - if (selected) { - Box(Modifier.fillMaxSize().background(Color.White.copy(alpha = 0.6f), CircleShape) - .border(3.dp, Color(0xFF4DBB35), CircleShape)) - } - } - } - } + ProfilePictureOptions( + selectedPicture = selectedPicture, + onPictureSelected = onPictureSelected + ) } } } diff --git a/app/src/main/java/com/cornellappdev/score/screen/OnboardingProfileSelectScreen.kt b/app/src/main/java/com/cornellappdev/score/screen/OnboardingProfileSelectScreen.kt new file mode 100644 index 0000000..1dfa4a7 --- /dev/null +++ b/app/src/main/java/com/cornellappdev/score/screen/OnboardingProfileSelectScreen.kt @@ -0,0 +1,170 @@ +package com.cornellappdev.score.screen + +import androidx.compose.foundation.Image +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.BoxWithConstraints +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.material3.Button +import androidx.compose.material3.ButtonDefaults +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.dp +import com.cornellappdev.score.R +import com.cornellappdev.score.theme.CrimsonPrimary +import com.cornellappdev.score.theme.Style + +@Composable +fun OnboardingProfileSelectScreen( + onNext: (ProfilePicture) -> Unit, + modifier: Modifier = Modifier, + displayName: String? = null +) { + var selectedPicture by rememberSaveable { mutableStateOf(ProfilePicture.TENNIS) } + val name = displayName ?: stringResource(R.string.onboarding_default_name) + + BoxWithConstraints( + modifier = modifier + .fillMaxSize() + .background(Color.White) + ) { + val designScale = minOf(maxWidth.value / 393f, maxHeight.value / 852f) + val designDp: (Float) -> Dp = { (it * designScale).dp } + + Column(modifier = Modifier.fillMaxSize()) { + Column( + modifier = Modifier + .fillMaxWidth() + .padding(top = designDp(79f)), + horizontalAlignment = Alignment.CenterHorizontally + ) { + Text( + text = stringResource(R.string.edit_profile), + style = Style.heading1, + color = Color(0xFF333333) + ) + Spacer(Modifier.height(designDp(4f))) + Text( + text = stringResource(R.string.onboarding_profile_subtitle), + style = Style.bodyMedium, + color = Color(0xFF333333) + ) + + Spacer(Modifier.height(designDp(47f))) + Image( + painter = painterResource(selectedPicture.drawable), + contentDescription = stringResource( + R.string.selected_profile_picture, + stringResource(selectedPicture.label) + ), + modifier = Modifier.size(designDp(100f)) + ) + Spacer(Modifier.height(designDp(16f))) + Text( + text = name, + style = Style.bodyMedium, + color = Color.Black + ) + + Spacer(Modifier.height(designDp(60f))) + Text( + text = stringResource(R.string.onboarding_select_profile_picture), + style = Style.metricMedium, + color = Color.Black, + textAlign = TextAlign.Center, + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 24.dp) + ) + Spacer(Modifier.height(designDp(24f))) + ProfilePictureOptions( + selectedPicture = selectedPicture, + onPictureSelected = { selectedPicture = it }, + modifier = Modifier.fillMaxWidth() + ) + } + + Spacer(Modifier.weight(1f)) + OnboardingProfileFooter( + onNext = { onNext(selectedPicture) }, + designDp = designDp + ) + } + } +} + +@Composable +private fun OnboardingProfileFooter( + onNext: () -> Unit, + designDp: (Float) -> Dp +) { + Column( + modifier = Modifier + .fillMaxWidth() + .padding( + start = 24.dp, + end = 24.dp, + top = designDp(24f), + bottom = designDp(60f) + ), + horizontalAlignment = Alignment.CenterHorizontally + ) { + Button( + onClick = onNext, + modifier = Modifier + .fillMaxWidth() + .height(designDp(48f)), + shape = CircleShape, + colors = ButtonDefaults.buttonColors( + containerColor = CrimsonPrimary, + contentColor = Color.White + ) + ) { + Text( + text = stringResource(R.string.next), + style = Style.heading3, + color = Color.White + ) + } + + Spacer(Modifier.height(designDp(16f))) + RowProgressDots() + } +} + +@Composable +private fun RowProgressDots() { + androidx.compose.foundation.layout.Row( + horizontalArrangement = Arrangement.spacedBy(4.dp), + verticalAlignment = Alignment.CenterVertically + ) { + Box(Modifier.size(8.dp).background(CrimsonPrimary, CircleShape)) + Box(Modifier.size(8.dp).background(Color(0xFFEAEAEA), CircleShape)) + } +} + +@Preview(showBackground = true, widthDp = 393, heightDp = 852) +@Composable +private fun OnboardingProfileSelectScreenPreview() { + OnboardingProfileSelectScreen(onNext = {}) +} diff --git a/app/src/main/java/com/cornellappdev/score/screen/ProfilePictureOptions.kt b/app/src/main/java/com/cornellappdev/score/screen/ProfilePictureOptions.kt new file mode 100644 index 0000000..ed5fcc2 --- /dev/null +++ b/app/src/main/java/com/cornellappdev/score/screen/ProfilePictureOptions.kt @@ -0,0 +1,87 @@ +package com.cornellappdev.score.screen + +import androidx.compose.foundation.Image +import androidx.compose.foundation.background +import androidx.compose.foundation.border +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.widthIn +import androidx.compose.foundation.selection.selectable +import androidx.compose.foundation.selection.selectableGroup +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.semantics.Role +import androidx.compose.ui.unit.dp +import com.cornellappdev.score.components.ScorePreview + +/** + * Shared profile picture choices used by profile editing and first-time onboarding. + */ +@Composable +fun ProfilePictureOptions( + selectedPicture: ProfilePicture, + onPictureSelected: (ProfilePicture) -> Unit, + modifier: Modifier = Modifier +) { + Row( + modifier = modifier + .widthIn(max = 393.dp) + .fillMaxWidth() + .padding(horizontal = 24.dp) + .selectableGroup(), + horizontalArrangement = Arrangement.SpaceBetween + ) { + ProfilePicture.entries.forEach { picture -> + val selected = picture == selectedPicture + Box( + modifier = Modifier + .size(70.dp) + .clip(CircleShape) + .selectable( + selected = selected, + role = Role.RadioButton, + onClick = { onPictureSelected(picture) } + ), + contentAlignment = Alignment.Center + ) { + Image( + painter = painterResource(picture.drawable), + contentDescription = stringResource(picture.label), + modifier = Modifier.fillMaxSize() + ) + if (selected) { + Box( + Modifier + .fillMaxSize() + .background(Color.White.copy(alpha = 0.6f), CircleShape) + .border(3.dp, Color(0xFF55B63C), CircleShape) + ) + } + } + } + } +} + +@Preview(showBackground = true, widthDp = 393) +@Composable +private fun ProfilePictureOptionsPreview() { + ScorePreview(backgroundColor = Color.White) { + ProfilePictureOptions( + selectedPicture = ProfilePicture.TENNIS, + onPictureSelected = {}, + modifier = Modifier.fillMaxWidth() + ) + } +} diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index c264cc3..474c141 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -7,4 +7,8 @@ Tennis Baseball Soccer + Claire Yu + You can change this later in the profile page. + Select profile picture: + Next \ No newline at end of file