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

Material 1.1 release update #1129

Merged
merged 6 commits into from
May 11, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
9 changes: 1 addition & 8 deletions Reply/app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -87,12 +87,6 @@ android {
kotlinCompilerExtensionVersion = libs.versions.compose.compiler.get()
}

packaging.resources {
// Multiple dependency bring these files in. Exclude them to enable
// our test APK to build (has no effect on our AARs)
excludes += "/META-INF/AL2.0"
excludes += "/META-INF/LGPL2.1"
}
}

dependencies {
Expand All @@ -107,8 +101,7 @@ dependencies {
implementation(libs.androidx.compose.ui.tooling.preview)
debugImplementation(libs.androidx.compose.ui.tooling)

// custom declaration for latest versions of material 3 and adaptive accompanist
implementation("androidx.compose.material3:material3:1.0.0-rc01")
implementation(libs.androidx.compose.material3)
implementation("com.google.accompanist:accompanist-adaptive:0.26.2-beta")

implementation(libs.androidx.compose.materialWindow)
Expand Down
44 changes: 29 additions & 15 deletions Reply/app/src/main/java/com/example/reply/ui/ReplyListContent.kt
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,9 @@ import androidx.window.layout.DisplayFeature
import com.example.reply.R
import com.example.reply.data.Email
import com.example.reply.ui.components.EmailDetailAppBar
import com.example.reply.ui.components.ReplyDockedSearchBar
import com.example.reply.ui.components.ReplyEmailListItem
import com.example.reply.ui.components.ReplyEmailThreadItem
import com.example.reply.ui.components.ReplySearchBar
import com.example.reply.ui.utils.ReplyContentType
import com.example.reply.ui.utils.ReplyNavigationType
import com.google.accompanist.adaptive.HorizontalTwoPaneStrategy
Expand Down Expand Up @@ -167,20 +167,34 @@ fun ReplyEmailList(
modifier: Modifier = Modifier,
navigateToDetail: (Long, ReplyContentType) -> Unit
) {
LazyColumn(modifier = modifier, state = emailLazyListState) {
item {
ReplySearchBar(modifier = Modifier.fillMaxWidth())
}
items(items = emails, key = { it.id }) { email ->
ReplyEmailListItem(
email = email,
navigateToDetail = { emailId ->
navigateToDetail(emailId, ReplyContentType.SINGLE_PANE)
},
toggleSelection = toggleEmailSelection,
isOpened = openedEmail?.id == email.id,
isSelected = selectedEmailIds.contains(email.id)
)
Box(modifier = modifier) {
ReplyDockedSearchBar(
emails = emails,
onSearchItemSelected = { searchedEmail ->
navigateToDetail(searchedEmail.id, ReplyContentType.SINGLE_PANE)
},
modifier = Modifier
.fillMaxWidth()
.padding(16.dp)
)

LazyColumn(
modifier = modifier
.fillMaxWidth()
.padding(top = 80.dp),
state = emailLazyListState
) {
items(items = emails, key = { it.id }) { email ->
ReplyEmailListItem(
email = email,
navigateToDetail = { emailId ->
navigateToDetail(emailId, ReplyContentType.SINGLE_PANE)
},
toggleSelection = toggleEmailSelection,
isOpened = openedEmail?.id == email.id,
isSelected = selectedEmailIds.contains(email.id)
)
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,27 +16,37 @@

package com.example.reply.ui.components

import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ArrowBack
import androidx.compose.material.icons.filled.MoreVert
import androidx.compose.material.icons.filled.Search
import androidx.compose.material3.DockedSearchBar
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.FilledIconButton
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.IconButtonDefaults
import androidx.compose.material3.ListItem
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateListOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
Expand All @@ -46,35 +56,111 @@ import com.example.reply.data.Email

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun ReplySearchBar(modifier: Modifier = Modifier) {
Row(
modifier = modifier
.fillMaxWidth()
.padding(top = 24.dp, bottom = 16.dp, start = 16.dp, end = 16.dp)
.background(MaterialTheme.colorScheme.surface, CircleShape),
verticalAlignment = Alignment.CenterVertically
fun ReplyDockedSearchBar(
emails: List<Email>,
onSearchItemSelected: (Email) -> Unit,
modifier: Modifier = Modifier
) {
var query by remember { mutableStateOf("") }
var active by remember { mutableStateOf(false) }
val searchResults = remember { mutableStateListOf<Email>() }

LaunchedEffect(query) {
searchResults.clear()
if (query.isNotEmpty()) {
searchResults.addAll(
emails.filter {
it.subject.startsWith(
prefix = query,
ignoreCase = true
) || it.sender.fullName.startsWith(
prefix =
query,
ignoreCase = true
)
}
)
}
}

DockedSearchBar(
modifier = modifier,
query = query,
onQueryChange = {
query = it
},
onSearch = { active = false },
active = active,
onActiveChange = {
active = it
},
placeholder = { Text(text = stringResource(id = R.string.search_emails)) },
leadingIcon = {
if (active) {
Icon(
imageVector = Icons.Default.ArrowBack,
contentDescription = stringResource(id = R.string.back_button),
modifier = Modifier
.padding(start = 16.dp)
.clickable {
active = false
query = ""
},
)
} else {
Icon(
imageVector = Icons.Default.Search,
contentDescription = stringResource(id = R.string.search),
modifier = Modifier.padding(start = 16.dp),
)
}
},
trailingIcon = {
ReplyProfileImage(
drawableResource = R.drawable.avatar_6,
description = stringResource(id = R.string.profile),
modifier = Modifier
.padding(12.dp)
.size(32.dp)
)
},
) {
Icon(
imageVector = Icons.Default.Search,
contentDescription = stringResource(id = R.string.search),
modifier = Modifier.padding(start = 16.dp),
tint = MaterialTheme.colorScheme.outline
)
Text(
text = stringResource(id = R.string.search_replies),
modifier = Modifier
.weight(1f)
.padding(16.dp),
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.outline
)
ReplyProfileImage(
drawableResource = R.drawable.avatar_6,
description = stringResource(id = R.string.profile),
modifier = Modifier
.padding(12.dp)
.size(32.dp)
)
if (searchResults.isNotEmpty()) {
LazyColumn(
modifier = Modifier.fillMaxWidth(),
contentPadding = PaddingValues(16.dp),
verticalArrangement = Arrangement.spacedBy(4.dp)
) {
items(items = searchResults, key = { it.id }) { email ->
ListItem(
headlineContent = { Text(email.subject) },
supportingContent = { Text(email.sender.fullName) },
leadingContent = {
ReplyProfileImage(
drawableResource = email.sender.avatar,
description = stringResource(id = R.string.profile),
modifier = Modifier
.size(32.dp)
)
},
modifier = Modifier.clickable {
onSearchItemSelected.invoke(email)
query = ""
active = false
}
)
}
}
} else if (query.isNotEmpty()) {
Text(
text = stringResource(id = R.string.no_item_found),
modifier = Modifier.padding(16.dp)
)
} else
Text(
text = stringResource(id = R.string.no_search_history),
modifier = Modifier.padding(16.dp)
)
}
}

Expand Down
5 changes: 4 additions & 1 deletion Reply/app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@

<string name="profile">Profile</string>
<string name="search">Search</string>
<string name="search_replies">Search replies</string>
<string name="reply">Reply</string>
<string name="reply_all">Reply All</string>

Expand All @@ -32,4 +31,8 @@
<string name="more_options_button">More options</string>
<string name="messages">Messages</string>
<string name="four_hours_ago">4 hrs ago</string>

<string name="search_emails">Search emails</string>
<string name="no_item_found">No item found</string>
<string name="no_search_history">No search history</string>
</resources>