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

epic/18-details-photo-screen #26

Merged
merged 12 commits into from
Apr 13, 2024
3 changes: 3 additions & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -43,14 +43,17 @@ android {
dependencies {
implementation(projects.core.commonUi)
implementation(projects.core.data)
implementation(projects.features.home.domain)
implementation(projects.features.home.ui)
implementation(projects.features.detail.ui)

implementation(libs.androidx.core.splashscreen)
implementation(libs.androidx.core.ktx)

implementation(libs.androidx.activity.compose)

implementation(libs.androidx.compose.material3)
implementation(libs.androidx.navigation.compose)

implementation(libs.coil.kt)

Expand Down
1 change: 1 addition & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.PurrfectPics.Splash"
android:windowSoftInputMode="adjustResize"
tools:targetApi="31">

<activity
Expand Down
12 changes: 7 additions & 5 deletions app/src/main/kotlin/com/manuelnunez/apps/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@ package com.manuelnunez.apps
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.activity.viewModels
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
import com.manuelnunez.apps.core.ui.component.MainGradientBackground
import androidx.navigation.compose.rememberNavController
import com.manuelnunez.apps.core.ui.theme.MainTheme
import com.manuelnunez.apps.features.home.ui.HomeView
import com.manuelnunez.apps.navigation.MainNavigation
import dagger.hilt.android.AndroidEntryPoint

@AndroidEntryPoint
Expand All @@ -20,10 +21,11 @@ class MainActivity : ComponentActivity() {

splashScreen.setKeepOnScreenCondition { viewModel.isLoading.value }

enableEdgeToEdge()

setContent {
MainTheme(disableDynamicTheming = true) {
MainGradientBackground { HomeView(navigateToDetails = {}, navigateToSeeMore = {}) }
}
val navController = rememberNavController()
MainTheme { MainNavigation(navController = navController) }
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package com.manuelnunez.apps.navigation

import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.navigation.NavHostController
import androidx.navigation.compose.NavHost
import com.manuelnunez.apps.feature.detail.ui.navigation.detailScreen
import com.manuelnunez.apps.feature.detail.ui.navigation.navigateToDetail
import com.manuelnunez.apps.features.home.ui.navigation.HOME_ROUTE
import com.manuelnunez.apps.features.home.ui.navigation.homeScreen

@Composable
fun MainNavigation(
modifier: Modifier = Modifier,
navController: NavHostController,
startDestination: String = HOME_ROUTE,
) {
NavHost(modifier = modifier, navController = navController, startDestination = startDestination) {
homeScreen(navigateToDetails = navController::navigateToDetail, navigateToSeeMore = {})
detailScreen(onBackClick = { navController.navigateUp() })
}
}
13 changes: 13 additions & 0 deletions app/src/main/res/values/splash.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools">

<style name="NightAdjusted.Theme.Splash" parent="Theme.SplashScreen">
<item name="android:windowLightStatusBar" tools:targetApi="23">true</item>
<item name="android:windowLightNavigationBar" tools:targetApi="27">true</item>
</style>

<style name="Theme.PurrfectPics.Splash" parent="NightAdjusted.Theme.Splash">
<item name="windowSplashScreenAnimatedIcon">@drawable/ic_splash</item>
<item name="postSplashScreenTheme">@style/Theme.PurrfectPics</item>
</style>
</resources>
12 changes: 1 addition & 11 deletions app/src/main/res/values/themes.xml
Original file line number Diff line number Diff line change
@@ -1,16 +1,6 @@
<resources xmlns:tools="http://schemas.android.com/tools">
<resources>
<!-- Base application theme. -->
<style name="NightAdjusted.Theme.PurrfectPics" parent="android:Theme.Material.Light.NoActionBar" />

<style name="Theme.PurrfectPics" parent="NightAdjusted.Theme.PurrfectPics" />

<style name="NightAdjusted.Theme.Splash" parent="Theme.SplashScreen">
<item name="android:windowLightStatusBar" tools:targetApi="23">true</item>
<item name="android:windowLightNavigationBar" tools:targetApi="27">true</item>
</style>

<style name="Theme.PurrfectPics.Splash" parent="NightAdjusted.Theme.Splash">
<item name="windowSplashScreenAnimatedIcon">@drawable/ic_splash</item>
<item name="postSplashScreenTheme">@style/Theme.PurrfectPics</item>
</style>
</resources>
2 changes: 2 additions & 0 deletions core/common-ui/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ dependencies {

implementation(libs.coil.kt.compose)

implementation(libs.androidx.navigation.compose)

// Compose
val composeBom = platform(libs.androidx.compose.bom)
implementation(composeBom)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.manuelnunez.apps.core.ui

import android.os.Bundle
import androidx.navigation.NavController
import androidx.navigation.NavOptions
import androidx.navigation.Navigator

fun NavController.navigate(
route: String,
args: Bundle,
navOptions: NavOptions? = null,
navigatorExtras: Navigator.Extras? = null
) {
val nodeId = graph.findNode(route = route)?.id
if (nodeId != null) {
navigate(nodeId, args, navOptions, navigatorExtras)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.manuelnunez.apps.core.ui.component

import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import kotlin.math.roundToInt

@Composable
fun AdaptableVerticalGrid(
modifier: Modifier = Modifier,
decoration: AdaptableVerticalGridDecoration,
content: @Composable () -> Unit
) {
val config = LocalConfiguration.current

val screenWidth =
config.screenWidthDp.dp - (decoration.gridPadding - decoration.itemHorizontalMargin) * 2
val columns =
(screenWidth / (decoration.itemWidth + decoration.itemHorizontalMargin * 2)).roundToInt()

VerticalGrid(modifier, columns, content)
}

data class AdaptableVerticalGridDecoration(
val gridPadding: Dp,
val itemWidth: Dp,
val itemHorizontalMargin: Dp,
)
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package com.manuelnunez.apps.core.ui.component

import android.content.res.Configuration
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.size
Expand All @@ -15,13 +14,13 @@ import androidx.compose.ui.draw.drawWithCache
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import com.manuelnunez.apps.core.ui.theme.GradientColors
import com.manuelnunez.apps.core.ui.theme.LocalBackgroundTheme
import com.manuelnunez.apps.core.ui.theme.LocalGradientColors
import com.manuelnunez.apps.core.ui.theme.MainTheme
import com.manuelnunez.apps.core.ui.utils.ThemePreviews
import kotlin.math.tan

/**
Expand Down Expand Up @@ -124,37 +123,29 @@ fun MainGradientBackground(
}
}

/**
* Multipreview annotation that represents light and dark themes. Add this annotation to a
* composable to render the both themes.
*/
@Preview(uiMode = Configuration.UI_MODE_NIGHT_NO, name = "Light theme")
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES, name = "Dark theme")
annotation class ThemePreviews

@ThemePreviews
@Composable
fun BackgroundDefault() {
fun BackgroundDefaultPreview() {
MainTheme(disableDynamicTheming = true) { MainBackground(Modifier.size(100.dp), content = {}) }
}

@ThemePreviews
@Composable
fun BackgroundDynamic() {
fun BackgroundDynamicPreview() {
MainTheme(disableDynamicTheming = false) { MainBackground(Modifier.size(100.dp), content = {}) }
}

@ThemePreviews
@Composable
fun GradientBackgroundDefault() {
fun GradientBackgroundDefaultPreview() {
MainTheme(disableDynamicTheming = true) {
MainGradientBackground(Modifier.size(100.dp), content = {})
}
}

@ThemePreviews
@Composable
fun GradientBackgroundDynamic() {
fun GradientBackgroundDynamicPreview() {
MainTheme(disableDynamicTheming = false) {
MainGradientBackground(Modifier.size(100.dp), content = {})
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ import com.manuelnunez.apps.core.ui.R
/** A wrapper around [AsyncImage] which determines the colorFilter based on the theme */
@Composable
fun DynamicAsyncImage(
imageUrl: String,
modifier: Modifier = Modifier,
imageUrl: String,
contentDescription: String,
placeholder: Painter = painterResource(R.drawable.ic_broken_image),
contentScale: ContentScale = ContentScale.Crop
Expand All @@ -42,6 +42,7 @@ fun DynamicAsyncImage(
isError = state is Error
})
val isLocalInspection = LocalInspectionMode.current

Box(
modifier = modifier,
contentAlignment = Alignment.Center,
Expand All @@ -53,6 +54,7 @@ fun DynamicAsyncImage(
color = MaterialTheme.colorScheme.tertiary,
)
}

Image(
modifier = modifier,
painter = if (isError.not() && !isLocalInspection) imageLoader else placeholder,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package com.manuelnunez.apps.core.ui.component

import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.CardElevation
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.semantics.contentDescription
import androidx.compose.ui.semantics.semantics
import com.manuelnunez.apps.core.ui.theme.MainTheme
import com.manuelnunez.apps.core.ui.utils.ThemePreviews

@Composable
fun ImageCard(
modifier: Modifier = Modifier,
imageUrl: String,
cardContentDescription: String,
elevation: CardElevation = CardDefaults.cardElevation(),
contentScale: ContentScale = ContentScale.Crop,
onClick: (() -> Unit)? = null,
) {
Card(
modifier =
modifier.clickable(onClick = { onClick?.invoke() }).semantics {
contentDescription = cardContentDescription
},
elevation = elevation) {
DynamicAsyncImage(
modifier = Modifier.fillMaxSize(),
imageUrl = imageUrl,
contentDescription = "",
contentScale = contentScale)
}
}

@ThemePreviews
@Composable
fun ImageCardPreview() {
MainTheme {
ImageCard(
onClick = {},
cardContentDescription = "",
imageUrl = "https://picsum.photos/id/237/200/300")
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package com.manuelnunez.apps.core.ui.component

import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.semantics.contentDescription
import androidx.compose.ui.semantics.semantics
import com.manuelnunez.apps.core.ui.theme.MainTheme
import com.manuelnunez.apps.core.ui.utils.ThemePreviews

@Composable
fun TextCard(modifier: Modifier = Modifier, text: String, onClick: () -> Unit) {
Card(
colors =
CardDefaults.cardColors().copy(containerColor = MaterialTheme.colorScheme.onBackground),
modifier =
modifier.clickable(onClick = { onClick.invoke() }).semantics {
contentDescription = text
}) {
Box(contentAlignment = Alignment.Center, modifier = Modifier.fillMaxSize()) {
Text(text = text, color = MaterialTheme.colorScheme.background)
}
}
}

@ThemePreviews
@Composable
fun TextCardPreview() {
MainTheme { TextCard(onClick = {}, text = "See more") }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.manuelnunez.apps.core.ui.component

import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import com.manuelnunez.apps.core.ui.theme.MainTheme
import com.manuelnunez.apps.core.ui.utils.ThemePreviews

@Composable
fun TitleText(modifier: Modifier = Modifier, title: String) {
Text(
modifier = modifier,
text = title,
color = MaterialTheme.colorScheme.onSurface,
style = MaterialTheme.typography.titleLarge)
}

@ThemePreviews
@Composable
fun TitleTextPreview() {
MainTheme { TitleText(title = "See more") }
}
Loading
Loading