Starter Code for Android Basic with Compose sample App - Replay
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.
- Experience with Kotlin syntax.
- How to create and run a project in Android Studio.
- How to create composable functions
- How to create compose navigation
- Install Android Studio, if you don't already have it.
- Download the sample.
- Import the sample into Android Studio.
- Build and run the sample.
@Preview(showBackground = true, showSystemUi = true)
@Composable
fun ReplyAppCompactPreview() {
ReplyTheme {
ReplyApp(windowSize = WindowWidthSizeClass.Compact)
}
}
ReplyApp(
windowSize = windowSize.widthSizeClass,
)
@Composable
fun ReplyProfileImage(
@DrawableRes drawableResource: Int,
description: String,
modifier: Modifier = Modifier,
) {
Image(
modifier = modifier.clip(CircleShape),
painter = painterResource(drawableResource),
contentDescription = description,
)
}
@Composable
fun ReplyLogo(
modifier: Modifier = Modifier,
color: Color = MaterialTheme.colorScheme.primary
) {
Image(
painter = painterResource(R.drawable.logo),
contentDescription = stringResource(R.string.logo),
colorFilter = ColorFilter.tint(color),
modifier = modifier
)
}
@Composable
private fun ReplyHomeTopBar(modifier: Modifier = Modifier) {
Row(
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically,
modifier = modifier
.fillMaxWidth()
.padding(vertical = 8.dp)
) {
ReplyLogo(
modifier = Modifier
.size(64.dp)
.padding(start = 4.dp)
)
ReplyProfileImage(
drawableResource = LocalAccountsDataProvider.userAccount.avatar,
description = stringResource(R.string.profile),
modifier = Modifier
.padding(end = 8.dp)
.size(48.dp)
)
}
}
@Composable
private fun ReplyEmailItemHeader(email: Email, modifier: Modifier = Modifier) {
Row(modifier = modifier.fillMaxWidth()) {
ReplyProfileImage(
drawableResource = email.sender.avatar,
description = email.sender.fullName,
modifier = Modifier.size(40.dp)
)
Column(
modifier = Modifier
.weight(1f)
.padding(horizontal = 12.dp, vertical = 4.dp),
verticalArrangement = Arrangement.Center
) {
Text(
text = email.sender.firstName,
style = MaterialTheme.typography.labelMedium
)
Text(
text = email.createdAt,
style = MaterialTheme.typography.labelMedium,
color = MaterialTheme.colorScheme.outline
)
}
}
}
@Composable
fun ReplyEmailListItem(
email: Email,
onCardClick: () -> Unit,
modifier: Modifier = Modifier
) {
Card(
modifier = modifier.padding(vertical = 4.dp),
onClick = onCardClick
) {
Column(
modifier = Modifier
.fillMaxWidth()
.padding(20.dp)
) {
ReplyEmailItemHeader(email)
Text(
text = email.subject,
style = MaterialTheme.typography.bodyLarge,
color = MaterialTheme.colorScheme.onSurface,
modifier = Modifier.padding(top = 12.dp, bottom = 8.dp),
)
Text(
text = email.body,
style = MaterialTheme.typography.bodyMedium,
maxLines = 2,
color = MaterialTheme.colorScheme.onSurfaceVariant,
overflow = TextOverflow.Ellipsis
)
}
}
}
@Composable
fun ReplyListOnlyContent(
replyUiState: ReplyUiState,
onEmailCardPressed: (Email) -> Unit,
modifier: Modifier = Modifier
) {
val emails = replyUiState.currentMailboxEmails
//list Emails with LazyColumn
LazyColumn(modifier = modifier.padding(horizontal = 16.dp)) {
item {
ReplyHomeTopBar(modifier = Modifier.fillMaxWidth())
}
items(emails, key = { email -> email.id }) { email ->
ReplyEmailListItem(
email = email,
onCardClick = {
onEmailCardPressed(email)
}
)
}
}
}
@Composable
private fun DetailsScreenHeader(email: Email, modifier: Modifier = Modifier) {
Row(modifier = modifier.fillMaxWidth()) {
ReplyProfileImage(
drawableResource = email.sender.avatar,
description = email.sender.fullName,
modifier = Modifier.size(40.dp)
)
Column(
modifier = Modifier
.weight(1f)
.padding(horizontal = 12.dp, vertical = 4.dp),
verticalArrangement = Arrangement.Center
) {
Text(
text = email.sender.firstName,
style = MaterialTheme.typography.labelMedium
)
Text(
text = email.createdAt,
style = MaterialTheme.typography.labelMedium,
color = MaterialTheme.colorScheme.outline
)
}
}
}
@Composable
private fun ActionButton(
text: String,
onButtonClicked: (String) -> Unit,
modifier: Modifier = Modifier,
containIrreversibleAction: Boolean = false
) {
Button(
onClick = { onButtonClicked(text) },
modifier = modifier
.fillMaxWidth()
.padding(vertical = 20.dp),
colors = ButtonDefaults.buttonColors(
containerColor =
if (!containIrreversibleAction)
MaterialTheme.colorScheme.inverseOnSurface
else MaterialTheme.colorScheme.onErrorContainer
)
) {
Text(
text = text,
color =
if (!containIrreversibleAction)
MaterialTheme.colorScheme.onSurfaceVariant
else MaterialTheme.colorScheme.onError
)
}
}
@Composable
private fun DetailsScreenButtonBar(
mailboxType: MailboxType,
displayToast: (String) -> Unit,
modifier: Modifier = Modifier
) {
when (mailboxType) {
MailboxType.Drafts ->
//add Action Button for Draft Messages
MailboxType.Spam ->
Row(
modifier = modifier
.fillMaxWidth()
.padding(vertical = 20.dp),
horizontalArrangement = Arrangement.spacedBy(4.dp),
) {
//add Action Button for Spam Messages
}
MailboxType.Sent, MailboxType.Inbox ->
Row(
modifier = modifier
.fillMaxWidth()
.padding(vertical = 20.dp),
horizontalArrangement = Arrangement.spacedBy(4.dp),
) {
//add Action Button for Sent Messages
}
}
}
@Composable
private fun ReplyEmailDetailsCard(
email: Email,
mailboxType: MailboxType,
modifier: Modifier = Modifier,
isFullScreen: Boolean = false
) {
val context = LocalContext.current
val displayToast = { text: String ->
Toast.makeText(context, text, Toast.LENGTH_SHORT).show()
}
Card(
modifier = modifier,
colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.surface)
) {
Column(
modifier = Modifier
.fillMaxWidth()
.padding(20.dp)
) {
//add Details Screen Header
DetailsScreenHeader(email)
if (!isFullScreen) {
Text(
text = email.subject,
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.outline,
modifier = Modifier.padding(top = 12.dp, bottom = 8.dp),
)
} else {
Spacer(modifier = Modifier.height(12.dp))
}
Text(
text = email.body,
style = MaterialTheme.typography.bodyLarge,
color = MaterialTheme.colorScheme.onSurfaceVariant,
)
//add Details ScreenButtonBar
DetailsScreenButtonBar(mailboxType, displayToast)
}
}
}
@Composable
private fun ReplyDetailsScreenTopBar(
onBackButtonClicked: () -> Unit,
replyUiState: ReplyUiState,
modifier: Modifier = Modifier
) {
Row(
modifier = modifier
.fillMaxWidth()
.padding(bottom = 24.dp),
verticalAlignment = Alignment.CenterVertically,
) {
IconButton(
onClick = onBackButtonClicked,
modifier = Modifier
.padding(horizontal = 16.dp)
.background(MaterialTheme.colorScheme.surface, shape = CircleShape),
) {
Icon(
imageVector = Icons.Default.ArrowBack,
contentDescription = stringResource(id = R.string.navigation_back)
)
}
Row(
horizontalArrangement = Arrangement.Center,
modifier = Modifier
.fillMaxWidth()
.padding(end = 40.dp)
) {
Text(
text = replyUiState.currentSelectedEmail.subject,
style = MaterialTheme.typography.titleMedium,
color = MaterialTheme.colorScheme.onSurfaceVariant
)
}
}
}
@Preview(showBackground = true, showSystemUi = true)
@Composable
fun ReplyAppCompactPreview() {
ReplyTheme {
ReplyApp(windowSize = WindowWidthSizeClass.Compact)
}
}
We used this app for educational Purpose only. All rights are served by Google Developer Team.
Copyright 2022 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
https://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.