Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
64ed51c
first draft of onboarding animation
amjiao Oct 14, 2026
f7bd25d
Complete animation
amjiao Oct 14, 2026
571bcac
Complete onboarding animation
amjiao Oct 16, 2026
cdfaf5e
remove onboarding preview screen from commit
amjiao Oct 16, 2026
2e6d25b
Clean up OnboardingScreen.kt
amjiao Oct 16, 2026
70345f9
Merge branch 'main' of https://github.com/cuappdev/score-android into…
amjiao Oct 16, 2026
539833c
Merge branch 'main' into onboarding
amjiao Oct 16, 2026
5a9d900
Onboarding profile picture selection
amjiao Oct 18, 2026
617b178
Onboarding profile picture selection
amjiao Oct 18, 2026
996fcf7
Merge branch 'onboarding2' of https://github.com/cuappdev/score-andro…
amjiao Oct 19, 2026
a645f2d
Make screen name more informative
amjiao Oct 19, 2026
f69c41e
Rename file
amjiao Oct 19, 2026
122ab35
Fix profile selector defaults
amjiao Oct 28, 2026
dc900f2
Change onboarding state representation from int to enum
amjiao Oct 28, 2026
ed21b71
Change manual scaling to adaptive
amjiao Oct 28, 2026
d580f98
Onboarding profile picture selection
amjiao Oct 18, 2026
3ebccad
Make screen name more informative
amjiao Oct 19, 2026
62db4eb
Rename file
amjiao Oct 19, 2026
247fd88
Fix profile selector defaults
amjiao Oct 28, 2026
39a605e
Change onboarding state representation from int to enum
amjiao Oct 28, 2026
dda86eb
Change manual scaling to adaptive
amjiao Oct 28, 2026
24d68df
Merge branch 'onboarding2' of https://github.com/cuappdev/score-andro…
amjiao Nov 1, 2026
b58f6bc
Merge branch 'onboarding' into onboarding2
amjiao Nov 2, 2026
fc6358c
restore onboarding profile selection
amjiao Nov 2, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 32 additions & 9 deletions app/src/main/java/com/cornellappdev/score/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,33 @@ import android.os.Bundle
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.edit
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
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.model.OnboardingPreferencesRepository
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
import com.google.firebase.analytics.analytics
import dagger.hilt.android.AndroidEntryPoint
import javax.inject.Inject

private const val APP_PREFERENCES = "score_app_preferences"
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() {
@Inject
Expand All @@ -30,27 +41,39 @@ class MainActivity : AppCompatActivity() {
enableEdgeToEdge()

val onboardingCompleted = onboardingPreferencesRepository.isOnboardingCompleted()
val preferences = getSharedPreferences(APP_PREFERENCES, MODE_PRIVATE)

setContent {
var showOnboarding by rememberSaveable {
mutableStateOf(!onboardingCompleted)
var onboardingStep by rememberSaveable {
mutableStateOf(
if (onboardingCompleted) OnboardingStep.COMPLETE else OnboardingStep.WELCOME
)
}

val completeOnboarding = remember {
{
onboardingPreferencesRepository.markOnboardingCompleted()
showOnboarding = false
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()
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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
)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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)
Comment thread
amjiao marked this conversation as resolved.
Resolved
Comment thread
amjiao marked this conversation as resolved.
Resolved

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))
Comment thread
amjiao marked this conversation as resolved.
Resolved
Box(Modifier.size(8.dp).background(GrayStroke, CircleShape))
}
}

@Preview(showBackground = true)
@Composable
private fun OnboardingProfileSelectScreenPreview() {
OnboardingProfileSelectScreen(onNext = {})
}
Original file line number Diff line number Diff line change
Expand Up @@ -719,33 +719,27 @@ private fun AnimatedSportIcon(
)
)

Box(modifier = Modifier.fillMaxSize()) {
Box(
Box(
modifier = Modifier
.offset(x = designDp(motion.left), y = designDp(motion.top))
.size(designDp(36f)),
contentAlignment = Alignment.Center
) {
Image(
painter = painterResource(motion.drawable),
contentDescription = null,
colorFilter = ColorFilter.tint(Color.White),
modifier = Modifier
.align(Alignment.Center)
.offset(
x = designDp(motion.left + 18f - 196.5f),
y = designDp(motion.top + 18f - 426f)
)
.size(designDp(36f)),
contentAlignment = Alignment.Center
) {
Image(
painter = painterResource(motion.drawable),
contentDescription = null,
colorFilter = ColorFilter.tint(Color.White),
modifier = Modifier
.size(designDp(36f))
.graphicsLayer {
this.alpha = alpha.coerceIn(0f, 1f)
scaleX = scale
scaleY = scale
rotationZ = rotation
translationX = designDp(x).toPx()
translationY = designDp(y).toPx()
}
)
}
.size(36.dp)
.graphicsLayer {
this.alpha = alpha.coerceIn(0f, 1f)
scaleX = scale
scaleY = scale
rotationZ = rotation
translationX = designDp(x).toPx()
translationY = designDp(y).toPx()
}
)
}
}

Expand Down
Loading
Loading