Skip to content
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
21 changes: 19 additions & 2 deletions stream-chat-android-compose-sample/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -66,54 +66,71 @@
<activity
android:name=".ui.login.UserLoginActivity"
android:exported="false"
android:label="@string/user_login_screen_title"
/>
<activity
android:name=".ui.login.CustomLoginActivity"
android:exported="false"
android:label="@string/user_login_advanced_options"
android:windowSoftInputMode="adjustResize"
/>
<activity
android:name=".ui.chats.ChatsActivity"
android:exported="false"
android:label="@string/app_bottom_bar_chats"
/>
<activity
android:name=".feature.channel.list.ChannelsActivity"
android:exported="false"
android:label="@string/app_bottom_bar_chats"
/>
<activity
android:name=".ui.channel.ChannelActivity"
android:exported="false"
android:label="@string/channel_title"
android:windowSoftInputMode="adjustResize"
/>
<activity
android:name=".ui.channel.DirectChannelInfoActivity"
android:exported="false"
android:label="@string/stream_ui_channel_info_contact_title"
/>
<activity
android:name=".ui.channel.GroupChannelInfoActivity"
android:exported="false"
android:label="@string/stream_ui_channel_info_group_title"
/>
<activity
android:name=".ui.pinned.PinnedMessagesActivity"
android:exported="false"
android:label="@string/pinned_messages_title"
/>
<activity
android:name=".feature.channel.add.AddChannelActivity"
android:exported="false"
android:label="@string/add_channel_title"
android:windowSoftInputMode="adjustResize"
/>
<activity
android:name=".feature.channel.add.group.AddGroupChannelActivity"
android:exported="false"
android:label="@string/add_group_channel_members_title"
android:windowSoftInputMode="adjustResize"
/>
<activity
android:name=".feature.channel.draft.DraftChannelActivity"
android:exported="false"
android:label="@string/stream_compose_channel_list_header_new_chat"
android:windowSoftInputMode="adjustResize"
/>
<activity android:name=".feature.reminders.MessageRemindersActivity" />
<activity android:name=".ui.profile.UserProfileActivity" />
<activity
android:name=".feature.reminders.MessageRemindersActivity"
android:label="@string/reminders_title"
/>
<activity
android:name=".ui.profile.UserProfileActivity"
android:label="@string/user_profile_title"
/>
<activity
android:name=".ui.channel.attachments.ChannelFilesAttachmentsActivity"
android:label="@string/stream_ui_channel_attachments_files_title"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,10 @@
<string name="channel_attachments_files_loading_more_error">Failed to load more files attachments</string>

<!-- Channel -->
<string name="channel_title">Channel</string>
<string name="channel_open_info">Open channel info</string>

<!-- User Profile -->
<string name="user_profile_title">Profile</string>

</resources>
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.paneTitle
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
Expand Down Expand Up @@ -101,11 +103,13 @@ internal fun AddMembersScreen(
onDismiss: () -> Unit,
onConfirm: () -> Unit,
) {
val paneTitleText = stringResource(id = R.string.stream_compose_add_members_title)
Column(
modifier = Modifier
.fillMaxSize()
.background(ChatTheme.colors.backgroundCoreApp)
.systemBarsPadding(),
.systemBarsPadding()
.semantics { paneTitle = paneTitleText },
) {
AddMembersHeader(
hasSelection = state.selectedUserIds.isNotEmpty(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.paneTitle
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
import androidx.lifecycle.compose.collectAsStateWithLifecycle
Expand Down Expand Up @@ -113,8 +115,9 @@ private fun DirectChannelInfoScaffold(
onViewAction: (action: ChannelInfoViewAction) -> Unit = {},
) {
val listState = rememberLazyListState()
val paneTitleText = stringResource(id = UiCommonR.string.stream_ui_channel_info_contact_title)
Scaffold(
modifier = modifier,
modifier = modifier.semantics { paneTitle = paneTitleText },
topBar = {
ChatTheme.componentFactory.DirectChannelInfoTopBar(
params = DirectChannelInfoTopBarParams(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalInspectionMode
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.paneTitle
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.text.TextRange
import androidx.compose.ui.text.input.TextFieldValue
import androidx.compose.ui.text.style.TextOverflow
Expand Down Expand Up @@ -206,7 +208,9 @@ private fun GroupChannelEditContent(
onSaveActionClick: () -> Unit = {},
onUploadPictureClick: () -> Unit = {},
) {
val paneTitleText = stringResource(id = UiCommonR.string.stream_ui_channel_info_edit_title)
Scaffold(
modifier = Modifier.semantics { paneTitle = paneTitleText },
topBar = {
GroupChannelEditTopBar(
isBusy = isBusy,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.pluralStringResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.paneTitle
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
import androidx.lifecycle.compose.collectAsStateWithLifecycle
Expand Down Expand Up @@ -174,8 +176,9 @@ private fun GroupChannelInfoScaffold(
onViewAction: (action: ChannelInfoViewAction) -> Unit = {},
) {
val listState = rememberLazyListState()
val paneTitleText = stringResource(id = UiCommonR.string.stream_ui_channel_info_group_title)
Scaffold(
modifier = modifier,
modifier = modifier.semantics { paneTitle = paneTitleText },
topBar = {
ChatTheme.componentFactory.GroupChannelInfoTopBar(
params = GroupChannelInfoTopBarParams(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.semantics.paneTitle
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
import io.getstream.chat.android.compose.state.channels.list.SearchQuery
Expand All @@ -67,7 +69,8 @@ import io.getstream.chat.android.ui.common.state.channels.actions.ViewInfo
* You can use the default implementation by not passing in an instance yourself, or you
* can customize the behavior using its parameters.
* @param viewModelKey Key to differentiate between instances of [ChannelListViewModel].
* @param title Header title.
* @param title Header title. Also drives the screen's `paneTitle` semantic, announced by TalkBack
* when the screen appears as a pane (e.g. an adaptive-layout pane or a Compose Navigation route).
* @param isShowingHeader If we show the header or hide it.
* @param searchMode The search mode for the screen.
* @param onHeaderActionClick Handler for the default header action.
Expand Down Expand Up @@ -123,7 +126,9 @@ public fun ChannelsScreen(
.testTag("Stream_ChannelsScreen"),
) {
Scaffold(
modifier = Modifier.fillMaxSize(),
modifier = Modifier
.fillMaxSize()
.semantics { paneTitle = title },
topBar = {
if (isShowingHeader) {
ChatTheme.componentFactory.ChannelListHeader(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.onSizeChanged
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.semantics.paneTitle
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.IntSize
import androidx.lifecycle.viewmodel.compose.viewModel
Expand Down Expand Up @@ -106,6 +108,8 @@ import kotlin.math.abs
* When the initial [ChannelViewModelFactory] is requested (before a channel is selected),
* `channelId`, `messageId`, and `parentMessageId` are `null`.
* @param title The title displayed in the list pane top bar. Default is `"Stream Chat"`.
* Also drives the list pane's `paneTitle` semantic, announced by TalkBack when the list pane
* appears or becomes active (e.g. switching between panes in the adaptive layout).
* @param searchMode The current search mode. Default is [SearchMode.None].
* @param listContentMode The mode for displaying the list content. Default is [ChatListContentMode.Channels].
* @param onBackPress Callback invoked when the user presses the back button.
Expand Down Expand Up @@ -179,10 +183,12 @@ public fun ChatsScreen(
onDispose { navigator.popUpTo(ThreePaneRole.List) }
}

val listPane = remember(listContentMode) {
val listPane = remember(listContentMode, title) {
movableContentOf { modifier: Modifier ->
Scaffold(
modifier = modifier.safeDrawingPadding(),
modifier = modifier
.safeDrawingPadding()
.semantics { paneTitle = title },
containerColor = ChatTheme.colors.backgroundCoreApp,
topBar = { listTopBarContent() },
bottomBar = { listBottomBarContent() },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.paneTitle
import androidx.compose.ui.semantics.semantics
import androidx.lifecycle.viewmodel.compose.viewModel
import io.getstream.chat.android.client.api.models.QueryThreadsRequest
import io.getstream.chat.android.compose.R
Expand All @@ -38,7 +40,8 @@ import io.getstream.chat.android.models.Thread
* It can be used without most parameters for default behavior, that can be tweaked if necessary.
*
* @param viewModelFactory The factory used to build the [ThreadListViewModel].
* @param title Header title.
* @param title Header title. Also drives the screen's `paneTitle` semantic, announced by TalkBack
* when the screen appears as a pane (e.g. an adaptive-layout pane or a Compose Navigation route).
* @param onHeaderAvatarClick Handle for when the user clicks on the header avatar.
* @param onThreadClick Handler for Thread item clicks.
*/
Expand All @@ -54,7 +57,7 @@ public fun ThreadsScreen(
val user by listViewModel.user.collectAsState()
val connectionState by listViewModel.connectionState.collectAsState()

Column(modifier = Modifier.fillMaxSize()) {
Column(modifier = Modifier.fillMaxSize().semantics { paneTitle = title }) {
ChatTheme.componentFactory.ThreadListHeader(
params = ThreadListHeaderParams(
title = title,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,4 +88,16 @@ internal class ChatsScreenTest : MockedChatClientTest {
composeTestRule.onNodeWithTag("Stream_ThreadListLoading")
.assertExists()
}

@Test
@UiThread
fun `with custom title`() {
composeTestRule.setContent {
ChatTheme {
ChatsScreen(title = "My Chats")
}
}

composeTestRule.onNodeWithText("My Chats").assertExists()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/*
* Copyright (c) 2014-2026 Stream.io Inc. All rights reserved.
*
* Licensed under the Stream License;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://github.com/GetStream/stream-chat-android/blob/main/LICENSE
*
* 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.
*/

package io.getstream.chat.android.compose.ui.threads

import androidx.annotation.UiThread
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.onNodeWithTag
import androidx.compose.ui.test.onNodeWithText
import androidx.test.ext.junit.runners.AndroidJUnit4
import io.getstream.chat.android.client.test.MockedChatClientTest
import io.getstream.chat.android.compose.ui.theme.ChatTheme
import io.getstream.chat.android.models.ConnectionState
import io.getstream.chat.android.randomUser
import kotlinx.coroutines.flow.MutableStateFlow
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.kotlin.doReturn
import org.mockito.kotlin.whenever
import org.robolectric.annotation.Config

@RunWith(AndroidJUnit4::class)
@Config(sdk = [33])
internal class ThreadsScreenTest : MockedChatClientTest {

@get:Rule
val composeTestRule = createComposeRule()

@Before
fun prepare() {
whenever(mockClientState.user) doReturn MutableStateFlow(randomUser())
whenever(mockClientState.connectionState) doReturn MutableStateFlow(ConnectionState.Connected)
}

@Test
@UiThread
fun `with default title`() {
composeTestRule.setContent {
ChatTheme {
ThreadsScreen()
}
}

composeTestRule.onNodeWithText("Threads").assertExists()
composeTestRule.onNodeWithTag("Stream_ThreadListLoading").assertExists()
}

@Test
@UiThread
fun `with custom title`() {
composeTestRule.setContent {
ChatTheme {
ThreadsScreen(title = "My Threads")
}
}

composeTestRule.onNodeWithText("My Threads").assertExists()
}
}
Loading