diff --git a/app/src/main/java/com/cornellappdev/score/MainActivity.kt b/app/src/main/java/com/cornellappdev/score/MainActivity.kt index 512793e..bed9942 100644 --- a/app/src/main/java/com/cornellappdev/score/MainActivity.kt +++ b/app/src/main/java/com/cornellappdev/score/MainActivity.kt @@ -9,7 +9,9 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue +import androidx.core.content.edit 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,9 +20,16 @@ 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 +private enum class OnboardingStep { + WELCOME, + PROFILE_SELECT, + COMPLETE +} + @AndroidEntryPoint class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { @@ -31,27 +40,36 @@ class MainActivity : AppCompatActivity() { val onboardingCompleted = preferences.getBoolean(KEY_ONBOARDING_COMPLETED, false) setContent { - var showOnboarding by rememberSaveable { - mutableStateOf(!onboardingCompleted) + var onboardingStep by rememberSaveable { + mutableStateOf(if (onboardingCompleted) OnboardingStep.COMPLETE else OnboardingStep.WELCOME) } val completeOnboarding = remember { { - preferences.edit() - .putBoolean(KEY_ONBOARDING_COMPLETED, true) - .apply() - showOnboarding = false + preferences.edit { + putBoolean(KEY_ONBOARDING_COMPLETED, true) + } + onboardingStep = OnboardingStep.COMPLETE } } - if (showOnboarding) { - OnboardingScreen( - onSignUp = completeOnboarding, + when (onboardingStep) { + OnboardingStep.WELCOME -> OnboardingScreen( + onSignUp = { onboardingStep = OnboardingStep.PROFILE_SELECT }, onLogIn = completeOnboarding, onLater = completeOnboarding ) - } else { - RootNavigation() + + OnboardingStep.PROFILE_SELECT -> OnboardingProfileSelectScreen( + onNext = { selectedPicture -> + preferences.edit { + putString(KEY_ONBOARDING_PROFILE_PICTURE, selectedPicture.name) + } + completeOnboarding() + } + ) + + OnboardingStep.COMPLETE -> 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..30d0223 --- /dev/null +++ b/app/src/main/java/com/cornellappdev/score/screen/OnboardingProfileSelectScreen.kt @@ -0,0 +1,166 @@ +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.GrayStroke +import com.cornellappdev.score.theme.Style + +@Composable +fun OnboardingProfileSelectScreen( + onNext: (ProfilePicture) -> Unit, + modifier: Modifier = Modifier, + displayName: String? = null +) { + var selectedPicture by rememberSaveable { mutableStateOf(ProfilePicture.BLANK) } + val name = displayName ?: stringResource(R.string.onboarding_default_name) + + Box( + modifier = modifier + .fillMaxSize() + .background(Color.White) + ) { + Column(modifier = Modifier.fillMaxSize()) { + Column( + modifier = Modifier + .fillMaxWidth() + .padding(top = 80.dp), + horizontalAlignment = Alignment.CenterHorizontally + ) { + Text( + text = stringResource(R.string.edit_profile), + style = Style.heading1, + color = Color(0xFF333333) + ) + Spacer(Modifier.height(4.dp)) + Text( + text = stringResource(R.string.onboarding_profile_subtitle), + style = Style.bodyMedium, + color = Color(0xFF333333) + ) + + Spacer(Modifier.height(40.dp)) + Image( + painter = painterResource(selectedPicture.drawable), + contentDescription = stringResource( + R.string.selected_profile_picture, + stringResource(selectedPicture.label) + ), + modifier = Modifier.size(100.dp) + ) + Spacer(Modifier.height(16.dp)) + Text( + text = name, + style = Style.bodyMedium, + color = Color.Black + ) + + Spacer(Modifier.height(60.dp)) + 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(24.dp)) + ProfilePictureOptions( + selectedPicture = selectedPicture, + onPictureSelected = { selectedPicture = it }, + modifier = Modifier.fillMaxWidth() + ) + } + + Spacer(Modifier.weight(1f)) + OnboardingProfileFooter( + onNext = { onNext(selectedPicture) } + ) + } + } +} + +@Composable +private fun OnboardingProfileFooter( + onNext: () -> Unit, +) { + Column( + modifier = Modifier + .fillMaxWidth() + .padding( + start = 24.dp, + end = 24.dp, + top = 24.dp, + bottom = 60.dp + ), + horizontalAlignment = Alignment.CenterHorizontally + ) { + Button( + onClick = onNext, + modifier = Modifier + .fillMaxWidth() + .height(48.dp), + 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(16.dp)) + 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(GrayStroke, CircleShape)) + } +} + +@Preview(showBackground = true) +@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..2caa255 --- /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 +import com.cornellappdev.score.theme.saturatedGreen + +/** + * 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 + .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, saturatedGreen, CircleShape) + ) + } + } + } + } +} + +@Preview(showBackground = true) +@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..9ef2641 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -7,4 +7,8 @@ Tennis Baseball Soccer + Your Name + You can change this later in the profile page. + Select profile picture: + Next \ No newline at end of file