diff --git a/README.md b/README.md index 734184d..db1afcd 100644 --- a/README.md +++ b/README.md @@ -4,14 +4,14 @@ Solution code for the Android Basics with Compose: Reply app. Introduction ------------ -The Reply app is a simple email client that display various categories of your -inbox. This app is used to illustrate the concept of adaptive layout. +The Reply app is a basic email client that displays various categories of your +inbox. This app is used to illustrate the concept of adaptive layouts. Pre-requisites -------------- -* Experience with Kotlin syntax. -* How to create and run a project in Android Studio. +* Experience with Kotlin syntax +* How to create and run a project in Android Studio * How to create composable functions * How to create compose navigation diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 3fbbb64..5fc73e0 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -39,4 +39,4 @@ - \ No newline at end of file + diff --git a/app/src/main/java/com/example/reply/MainActivity.kt b/app/src/main/java/com/example/reply/MainActivity.kt index 0fa6aca..0661340 100644 --- a/app/src/main/java/com/example/reply/MainActivity.kt +++ b/app/src/main/java/com/example/reply/MainActivity.kt @@ -51,9 +51,11 @@ class MainActivity : ComponentActivity() { @Composable fun ReplyAppCompactPreview() { ReplyTheme { - ReplyApp( - windowSize = WindowWidthSizeClass.Compact, - ) + Surface { + ReplyApp( + windowSize = WindowWidthSizeClass.Compact, + ) + } } } @@ -61,9 +63,11 @@ fun ReplyAppCompactPreview() { @Composable fun ReplyAppMediumPreview() { ReplyTheme { - ReplyApp( - windowSize = WindowWidthSizeClass.Medium, - ) + Surface { + ReplyApp( + windowSize = WindowWidthSizeClass.Medium, + ) + } } } @@ -71,8 +75,10 @@ fun ReplyAppMediumPreview() { @Composable fun ReplyAppExpandedPreview() { ReplyTheme { - ReplyApp( - windowSize = WindowWidthSizeClass.Expanded, - ) + Surface { + ReplyApp( + windowSize = WindowWidthSizeClass.Expanded, + ) + } } -} \ No newline at end of file +} diff --git a/app/src/main/java/com/example/reply/data/Account.kt b/app/src/main/java/com/example/reply/data/Account.kt index aa7dcd8..14f38b1 100644 --- a/app/src/main/java/com/example/reply/data/Account.kt +++ b/app/src/main/java/com/example/reply/data/Account.kt @@ -16,6 +16,7 @@ package com.example.reply.data import androidx.annotation.DrawableRes +import androidx.annotation.StringRes /** * A class which represents an account @@ -24,14 +25,11 @@ data class Account( /** Unique ID of a user **/ val id: Long, /** User's first name **/ - val firstName: Int, + @StringRes val firstName: Int, /** User's last name **/ - val lastName: Int, + @StringRes val lastName: Int, /** User's email address **/ - val email: Int, + @StringRes val email: Int, /** User's avatar image resource id **/ @DrawableRes val avatar: Int -) { - /** User's full name **/ - val fullName: String = "$firstName $lastName" -} \ No newline at end of file +) diff --git a/app/src/main/java/com/example/reply/data/Email.kt b/app/src/main/java/com/example/reply/data/Email.kt index e564e8a..00c4524 100644 --- a/app/src/main/java/com/example/reply/data/Email.kt +++ b/app/src/main/java/com/example/reply/data/Email.kt @@ -15,6 +15,8 @@ */ package com.example.reply.data +import androidx.annotation.StringRes + /** * A simple data class to represent an Email */ @@ -26,9 +28,9 @@ data class Email( /** Recipient(s) of the email **/ val recipients: List = emptyList(), /** Title of the email **/ - val subject: Int = -1, + @StringRes val subject: Int = -1, /** Content of the email **/ - val body: Int = -1, + @StringRes val body: Int = -1, /** Which mailbox it is in **/ var mailbox: MailboxType = MailboxType.Inbox, /** diff --git a/app/src/main/java/com/example/reply/data/MailboxType.kt b/app/src/main/java/com/example/reply/data/MailboxType.kt index f90d892..16e60ed 100644 --- a/app/src/main/java/com/example/reply/data/MailboxType.kt +++ b/app/src/main/java/com/example/reply/data/MailboxType.kt @@ -20,4 +20,4 @@ package com.example.reply.data */ enum class MailboxType { Inbox, Drafts, Sent, Spam -} \ No newline at end of file +} diff --git a/app/src/main/java/com/example/reply/data/local/LocalAccountsDataProvider.kt b/app/src/main/java/com/example/reply/data/local/LocalAccountsDataProvider.kt index 07e5c26..a3a9daf 100644 --- a/app/src/main/java/com/example/reply/data/local/LocalAccountsDataProvider.kt +++ b/app/src/main/java/com/example/reply/data/local/LocalAccountsDataProvider.kt @@ -114,4 +114,4 @@ object LocalAccountsDataProvider { return allUserContactAccounts.firstOrNull { it.id == accountId } ?: allUserContactAccounts.first() } -} \ No newline at end of file +} diff --git a/app/src/main/java/com/example/reply/ui/ReplyApp.kt b/app/src/main/java/com/example/reply/ui/ReplyApp.kt index 8f6516a..6590261 100644 --- a/app/src/main/java/com/example/reply/ui/ReplyApp.kt +++ b/app/src/main/java/com/example/reply/ui/ReplyApp.kt @@ -71,4 +71,4 @@ fun ReplyApp( }, modifier = modifier ) -} \ No newline at end of file +} diff --git a/app/src/main/java/com/example/reply/ui/ReplyDetailsScreen.kt b/app/src/main/java/com/example/reply/ui/ReplyDetailsScreen.kt index ef39586..169a379 100644 --- a/app/src/main/java/com/example/reply/ui/ReplyDetailsScreen.kt +++ b/app/src/main/java/com/example/reply/ui/ReplyDetailsScreen.kt @@ -54,8 +54,8 @@ import com.example.reply.data.MailboxType @Composable fun ReplyDetailsScreen( replyUiState: ReplyUiState, + onBackPressed: () -> Unit, modifier: Modifier = Modifier, - onBackPressed: () -> Unit = {}, isFullScreen: Boolean = false ) { BackHandler { @@ -83,10 +83,11 @@ fun ReplyDetailsScreen( email = replyUiState.currentSelectedEmail, mailboxType = replyUiState.currentMailbox, isFullScreen = isFullScreen, - modifier = if (isFullScreen) + modifier = if (isFullScreen) { Modifier.padding(horizontal = dimensionResource(R.dimen.detail_card_outer_padding_horizontal)) - else + } else { Modifier.padding(end = dimensionResource(R.dimen.detail_card_outer_padding_horizontal)) + } ) } } @@ -153,7 +154,9 @@ private fun ReplyEmailDetailsCard( email, Modifier.fillMaxWidth() ) - if (!isFullScreen) { + if (isFullScreen) { + Spacer(modifier = Modifier.height(dimensionResource(R.dimen.detail_content_padding_top))) + } else { Text( text = stringResource(email.subject), style = MaterialTheme.typography.bodyMedium, @@ -163,8 +166,6 @@ private fun ReplyEmailDetailsCard( bottom = dimensionResource(R.dimen.detail_expanded_subject_body_spacing) ), ) - } else { - Spacer(modifier = Modifier.height(dimensionResource(R.dimen.detail_content_padding_top))) } Text( text = stringResource(email.body), @@ -245,7 +246,8 @@ private fun DetailsScreenHeader(email: Email, modifier: Modifier = Modifier) { Row(modifier = modifier) { ReplyProfileImage( drawableResource = email.sender.avatar, - description = email.sender.fullName, + description = stringResource(email.sender.firstName) + " " + + stringResource(email.sender.lastName), modifier = Modifier.size( dimensionResource(R.dimen.email_header_profile_size) ) @@ -287,17 +289,20 @@ private fun ActionButton( .padding(vertical = dimensionResource(R.dimen.detail_action_button_padding_vertical)), colors = ButtonDefaults.buttonColors( containerColor = - if (!containIrreversibleAction) + if (containIrreversibleAction) { + MaterialTheme.colorScheme.onErrorContainer + } else { MaterialTheme.colorScheme.primaryContainer - else MaterialTheme.colorScheme.onErrorContainer + } ) ) { Text( text = text, - color = - if (!containIrreversibleAction) + color = if (containIrreversibleAction) { + MaterialTheme.colorScheme.onError + } else { MaterialTheme.colorScheme.onSurfaceVariant - else MaterialTheme.colorScheme.onError + } ) } } diff --git a/app/src/main/java/com/example/reply/ui/ReplyHomeContent.kt b/app/src/main/java/com/example/reply/ui/ReplyHomeContent.kt index 811e66f..23f4281 100644 --- a/app/src/main/java/com/example/reply/ui/ReplyHomeContent.kt +++ b/app/src/main/java/com/example/reply/ui/ReplyHomeContent.kt @@ -130,10 +130,11 @@ fun ReplyEmailListItem( Card( modifier = modifier, colors = CardDefaults.cardColors( - containerColor = if (selected) + containerColor = if (selected) { MaterialTheme.colorScheme.primaryContainer - else + } else { MaterialTheme.colorScheme.secondaryContainer + } ), onClick = onCardClick ) { @@ -171,7 +172,8 @@ private fun ReplyEmailItemHeader(email: Email, modifier: Modifier = Modifier) { Row(modifier = modifier) { ReplyProfileImage( drawableResource = email.sender.avatar, - description = email.sender.fullName, + description = stringResource(email.sender.firstName) + " " + + stringResource(email.sender.lastName), modifier = Modifier.size(dimensionResource(R.dimen.email_header_profile_size)) ) Column( diff --git a/app/src/main/java/com/example/reply/ui/ReplyHomeScreen.kt b/app/src/main/java/com/example/reply/ui/ReplyHomeScreen.kt index bd043c0..2e56627 100644 --- a/app/src/main/java/com/example/reply/ui/ReplyHomeScreen.kt +++ b/app/src/main/java/com/example/reply/ui/ReplyHomeScreen.kt @@ -33,7 +33,6 @@ import androidx.compose.material.icons.filled.Drafts import androidx.compose.material.icons.filled.Inbox import androidx.compose.material.icons.filled.Report import androidx.compose.material.icons.filled.Send -import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.NavigationBar @@ -60,7 +59,6 @@ import com.example.reply.data.local.LocalAccountsDataProvider import com.example.reply.ui.utils.ReplyContentType import com.example.reply.ui.utils.ReplyNavigationType -@OptIn(ExperimentalMaterial3Api::class) @Composable fun ReplyHomeScreen( navigationType: ReplyNavigationType, @@ -136,9 +134,9 @@ fun ReplyHomeScreen( } else { ReplyDetailsScreen( replyUiState = replyUiState, - isFullScreen = true, + onBackPressed = onDetailScreenBackPressed, modifier = modifier, - onBackPressed = onDetailScreenBackPressed + isFullScreen = true ) } } @@ -190,8 +188,7 @@ private fun ReplyAppContent( AnimatedVisibility( visible = navigationType == ReplyNavigationType.BOTTOM_NAVIGATION ) { - val bottomNavigationContentDescription = - stringResource(R.string.navigation_bottom) + val bottomNavigationContentDescription = stringResource(R.string.navigation_bottom) ReplyBottomNavigationBar( currentTab = replyUiState.currentMailbox, onTabPressed = onTabPressed, @@ -252,7 +249,6 @@ private fun ReplyBottomNavigationBar( } } -@OptIn(ExperimentalMaterial3Api::class) @Composable private fun NavigationDrawerContent( selectedDestination: MailboxType, @@ -313,4 +309,4 @@ private data class NavigationItemContent( val mailboxType: MailboxType, val icon: ImageVector, val text: String -) \ No newline at end of file +) diff --git a/app/src/main/java/com/example/reply/ui/ReplyViewModel.kt b/app/src/main/java/com/example/reply/ui/ReplyViewModel.kt index f392398..a3853da 100644 --- a/app/src/main/java/com/example/reply/ui/ReplyViewModel.kt +++ b/app/src/main/java/com/example/reply/ui/ReplyViewModel.kt @@ -69,4 +69,4 @@ class ReplyViewModel : ViewModel() { ) } } -} \ No newline at end of file +} diff --git a/app/src/main/java/com/example/reply/ui/theme/Color.kt b/app/src/main/java/com/example/reply/ui/theme/Color.kt index 4784aec..eab4efd 100644 --- a/app/src/main/java/com/example/reply/ui/theme/Color.kt +++ b/app/src/main/java/com/example/reply/ui/theme/Color.kt @@ -77,4 +77,3 @@ val md_theme_dark_inversePrimary = Color(0xFF006879) val md_theme_dark_surfaceTint = Color(0xFF54D7F3) val md_theme_dark_outlineVariant = Color(0xFF3F484B) val md_theme_dark_scrim = Color(0xFF000000) - diff --git a/app/src/main/java/com/example/reply/ui/theme/Theme.kt b/app/src/main/java/com/example/reply/ui/theme/Theme.kt index 8b5f551..753569a 100644 --- a/app/src/main/java/com/example/reply/ui/theme/Theme.kt +++ b/app/src/main/java/com/example/reply/ui/theme/Theme.kt @@ -63,7 +63,6 @@ private val LightColorScheme = lightColorScheme( scrim = md_theme_light_scrim, ) - private val DarkColorScheme = darkColorScheme( primary = md_theme_dark_primary, onPrimary = md_theme_dark_onPrimary, @@ -105,7 +104,11 @@ fun ReplyTheme( val colorScheme = when { dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> { val context = LocalContext.current - if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context) + if (darkTheme) { + dynamicDarkColorScheme(context) + } else { + dynamicLightColorScheme(context) + } } darkTheme -> DarkColorScheme @@ -125,4 +128,4 @@ fun ReplyTheme( typography = Typography, content = content ) -} \ No newline at end of file +} diff --git a/app/src/main/java/com/example/reply/ui/theme/Type.kt b/app/src/main/java/com/example/reply/ui/theme/Type.kt index 42a43ef..79b5863 100644 --- a/app/src/main/java/com/example/reply/ui/theme/Type.kt +++ b/app/src/main/java/com/example/reply/ui/theme/Type.kt @@ -30,4 +30,4 @@ val Typography = Typography( lineHeight = 24.sp, letterSpacing = 0.5.sp ) -) \ No newline at end of file +) diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml index 74bbfcc..111e06b 100644 --- a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml +++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml @@ -18,4 +18,4 @@ - \ No newline at end of file + diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml index 74bbfcc..111e06b 100644 --- a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml +++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml @@ -18,4 +18,4 @@ - \ No newline at end of file + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 4864d70..1b5b071 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -36,47 +36,47 @@ Jeff Hansen - hikingfan@gmail.com + hikingfan@altostrat.com Tracy Alvarez - tracealvie@gmail.com + tracealvie@altostrat.com Allison Trabucco - atrabucco222@gmail.com + atrabucco222@altostrat.com Ali Connors - aliconnors@gmail.com + aliconnors@altostrat.com Alberto Williams - albertowilliams124@gmail.com + albertowilliams124@altostrat.com Kim Alen - alen13@gmail.com + alen13@altostrat.com Google Express - express@google.com + express@altostrat.com Sandra Adams - sandraadams@gmail.com + sandraadams@altostrat.com Trevor Hansen - trevorhandsen@gmail.com + trevorhandsen@altostrat.com Sean Holt - sholt@gmail.com + sholt@altostrat.com Frank Hawkins - fhawkank@gmail.com + fhawkank@altostrat.com Package shipped! diff --git a/build.gradle.kts b/build.gradle.kts index 02609c7..f8c238f 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -13,13 +13,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + +// Top-level build file where you can add configuration options common to all sub-projects/modules. buildscript { extra.apply { set("lifecycle_version", "2.6.1") } } -// Top-level build file where you can add configuration options common to all sub-projects/modules. plugins { id("com.android.application") version "8.0.2" apply false id("com.android.library") version "8.0.2" apply false