Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make game's info header animatable using Compose's MotionLayout #245

Merged
merged 13 commits into from
Oct 23, 2024
2 changes: 1 addition & 1 deletion app/src/main/res/values-night/themes.xml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,6 @@
<style name="GamedgeThemeSplash" parent="Theme.SplashScreen">
<item name="windowSplashScreenBackground">@color/dark_colorPrimary</item>
<item name="windowSplashScreenAnimatedIcon">@drawable/ic_splash_light</item>
<item name="postSplashScreenTheme">@style/Theme.AppCompat.NoActionBar</item>
<item name="postSplashScreenTheme">@android:style/Theme.Material.NoActionBar</item>
</style>
</resources>
2 changes: 1 addition & 1 deletion app/src/main/res/values/themes.xml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,6 @@
<style name="GamedgeThemeSplash" parent="Theme.SplashScreen">
<item name="windowSplashScreenBackground">@color/light_colorPrimary</item>
<item name="windowSplashScreenAnimatedIcon">@drawable/ic_splash_dark</item>
<item name="postSplashScreenTheme">@style/Theme.AppCompat.Light.NoActionBar</item>
<item name="postSplashScreenTheme">@android:style/Theme.Material.Light.NoActionBar</item>
</style>
</resources>
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ class Spaces(
spacing_6_5: Dp = 26.dp,
spacing_7_0: Dp = 28.dp,
spacing_7_5: Dp = 30.dp,
spacing_8_0: Dp = 32.dp,
spacing_8_5: Dp = 34.dp,
spacing_9_0: Dp = 36.dp,
) {
var spacing_0_5 by mutableStateOf(spacing_0_5)
private set
Expand Down Expand Up @@ -73,6 +76,12 @@ class Spaces(
private set
var spacing_7_5 by mutableStateOf(spacing_7_5)
private set
var spacing_8_0 by mutableStateOf(spacing_8_0)
private set
var spacing_8_5 by mutableStateOf(spacing_8_5)
private set
var spacing_9_0 by mutableStateOf(spacing_9_0)
private set

override fun toString(): String {
return "Spaces(" +
Expand All @@ -90,7 +99,10 @@ class Spaces(
"spacing_6_0=$spacing_6_0, " +
"spacing_6_5=$spacing_6_5, " +
"spacing_7_0=$spacing_7_0, " +
"spacing_7_5=$spacing_7_5" +
"spacing_7_5=$spacing_7_5, " +
"spacing_8_0=$spacing_8_0, " +
"spacing_8_5=$spacing_8_5, " +
"spacing_9_0=$spacing_9_0" +
")"
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.asPaddingValues
import androidx.compose.foundation.layout.calculateEndPadding
import androidx.compose.foundation.layout.calculateStartPadding
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.navigationBars
import androidx.compose.foundation.layout.padding
Expand All @@ -32,13 +34,15 @@ import androidx.compose.foundation.layout.windowInsetsTopHeight
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.LazyItemScope
import androidx.compose.foundation.lazy.LazyListScope
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material.Scaffold
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalLayoutDirection
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
Expand Down Expand Up @@ -233,89 +237,83 @@ private fun SuccessState(
onCompanyClicked: (GameInfoCompanyUiModel) -> Unit,
onRelatedGameClicked: (GameInfoRelatedGameUiModel) -> Unit,
) {
LazyColumn(
modifier = Modifier.fillMaxWidth(),
contentPadding = contentPadding,
verticalArrangement = Arrangement.spacedBy(GamedgeTheme.spaces.spacing_3_5),
) {
headerItem(
model = gameInfo.headerModel,
onArtworkClicked = onArtworkClicked,
onBackButtonClicked = onBackButtonClicked,
onCoverClicked = onCoverClicked,
onLikeButtonClicked = onLikeButtonClicked,
)

if (gameInfo.hasVideos) {
videosItem(
videos = gameInfo.videoModels,
onVideoClicked = onVideoClicked,
)
}
val listState = rememberLazyListState()

GameInfoHeader(
headerInfo = gameInfo.headerModel,
listState = listState,
onArtworkClicked = onArtworkClicked,
onBackButtonClicked = onBackButtonClicked,
onCoverClicked = onCoverClicked,
onLikeButtonClicked = onLikeButtonClicked,
) { modifier ->
val layoutDirection = LocalLayoutDirection.current
val spacing = GamedgeTheme.spaces.spacing_3_5

LazyColumn(
modifier = modifier,
state = listState,
contentPadding = PaddingValues(
start = contentPadding.calculateStartPadding(layoutDirection),
top = contentPadding.calculateTopPadding().plus(spacing),
end = contentPadding.calculateEndPadding(layoutDirection),
bottom = contentPadding.calculateBottomPadding(),
),
verticalArrangement = Arrangement.spacedBy(spacing),
) {
if (gameInfo.hasVideos) {
videosItem(
videos = gameInfo.videoModels,
onVideoClicked = onVideoClicked,
)
}

if (gameInfo.hasScreenshots) {
screenshotsItem(
screenshots = gameInfo.screenshotModels,
onScreenshotClicked = onScreenshotClicked,
)
}
if (gameInfo.hasScreenshots) {
screenshotsItem(
screenshots = gameInfo.screenshotModels,
onScreenshotClicked = onScreenshotClicked,
)
}

if (gameInfo.hasSummary) {
summaryItem(model = checkNotNull(gameInfo.summary))
}
if (gameInfo.hasSummary) {
summaryItem(model = checkNotNull(gameInfo.summary))
}

if (gameInfo.hasDetails) {
detailsItem(model = checkNotNull(gameInfo.detailsModel))
}
if (gameInfo.hasDetails) {
detailsItem(model = checkNotNull(gameInfo.detailsModel))
}

if (gameInfo.hasLinks) {
linksItem(
model = gameInfo.linkModels,
onLinkClicked = onLinkClicked,
)
}
if (gameInfo.hasLinks) {
linksItem(
model = gameInfo.linkModels,
onLinkClicked = onLinkClicked,
)
}

if (gameInfo.hasCompanies) {
companiesItem(
model = gameInfo.companyModels,
onCompanyClicked = onCompanyClicked,
)
}
if (gameInfo.hasCompanies) {
companiesItem(
model = gameInfo.companyModels,
onCompanyClicked = onCompanyClicked,
)
}

if (gameInfo.hasOtherCompanyGames) {
relatedGamesItem(
model = checkNotNull(gameInfo.otherCompanyGames),
onGameClicked = onRelatedGameClicked,
)
}
if (gameInfo.hasOtherCompanyGames) {
relatedGamesItem(
model = checkNotNull(gameInfo.otherCompanyGames),
onGameClicked = onRelatedGameClicked,
)
}

if (gameInfo.hasSimilarGames) {
relatedGamesItem(
model = checkNotNull(gameInfo.similarGames),
onGameClicked = onRelatedGameClicked,
)
if (gameInfo.hasSimilarGames) {
relatedGamesItem(
model = checkNotNull(gameInfo.similarGames),
onGameClicked = onRelatedGameClicked,
)
}
}
}
}

private fun LazyListScope.headerItem(
model: GameInfoHeaderUiModel,
onArtworkClicked: (artworkIndex: Int) -> Unit,
onBackButtonClicked: () -> Unit,
onCoverClicked: () -> Unit,
onLikeButtonClicked: () -> Unit,
) {
gameInfoItem(item = GameInfoItem.Header) {
GameInfoHeader(
headerInfo = model,
onArtworkClicked = onArtworkClicked,
onBackButtonClicked = onBackButtonClicked,
onCoverClicked = onCoverClicked,
onLikeButtonClicked = onLikeButtonClicked,
)
}
}

private fun LazyListScope.videosItem(
videos: List<GameInfoVideoUiModel>,
onVideoClicked: (GameInfoVideoUiModel) -> Unit,
Expand Down Expand Up @@ -418,19 +416,18 @@ private enum class GameInfoItem(
val key: Int,
val contentType: Int,
) {
Header(key = 1, contentType = 1),
Videos(key = 2, contentType = 2),
Screenshots(key = 3, contentType = 3),
Summary(key = 4, contentType = 4),
Details(key = 5, contentType = 5),
Links(key = 6, contentType = 6),
Companies(key = 7, contentType = 7),
Videos(key = 1, contentType = 1),
Screenshots(key = 2, contentType = 2),
Summary(key = 3, contentType = 3),
Details(key = 4, contentType = 4),
Links(key = 5, contentType = 5),
Companies(key = 6, contentType = 6),

// Both other & similar games is the same composable
// filled with different data. That's why contentType
// is the same for them two.
OtherCompanyGames(key = 8, contentType = 8),
SimilarGames(key = 9, contentType = 8),
OtherCompanyGames(key = 7, contentType = 7),
SimilarGames(key = 8, contentType = 7),
}

// TODO (02.01.2022): Currently, preview height is limited to 2k DP.
Expand Down
Loading