- Add specific toolbar for threads

- Renamed RoomDetailFragment to TimelineFragment while it should be reused from threads
 - View root thread message in room functionality
This commit is contained in:
ariskotsomitopoulos 2021-11-22 14:02:17 +02:00
parent 3de0f7bf37
commit 586b3d8caa
38 changed files with 766 additions and 625 deletions

View file

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/coordinatorLayout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>

View file

@ -179,8 +179,7 @@
<activity android:name=".features.roomdirectory.RoomDirectoryActivity" />
<activity android:name=".features.roomdirectory.roompreview.RoomPreviewActivity" />
<activity android:name=".features.home.room.filtered.FilteredRoomsActivity" />
<activity android:name=".features.home.room.threads.RoomThreadsActivity" />
<activity android:name=".features.home.room.threads.detail.RoomThreadDetailActivity" />
<activity android:name=".features.home.room.threads.ThreadsActivity" />
<activity
android:name=".features.home.room.detail.RoomDetailActivity"

View file

@ -55,10 +55,10 @@ import im.vector.app.features.home.HomeDetailFragment
import im.vector.app.features.home.HomeDrawerFragment
import im.vector.app.features.home.LoadingFragment
import im.vector.app.features.home.room.breadcrumbs.BreadcrumbsFragment
import im.vector.app.features.home.room.detail.RoomDetailFragment
import im.vector.app.features.home.room.detail.TimelineFragment
import im.vector.app.features.home.room.detail.search.SearchFragment
import im.vector.app.features.home.room.list.RoomListFragment
import im.vector.app.features.home.room.threads.detail.RoomThreadDetailFragment
import im.vector.app.features.home.room.threads.detail.ThreadListFragment
import im.vector.app.features.login.LoginCaptchaFragment
import im.vector.app.features.login.LoginFragment
import im.vector.app.features.login.LoginGenericTextInputFormFragment
@ -183,8 +183,8 @@ interface FragmentModule {
@Binds
@IntoMap
@FragmentKey(RoomDetailFragment::class)
fun bindRoomDetailFragment(fragment: RoomDetailFragment): Fragment
@FragmentKey(TimelineFragment::class)
fun bindRoomDetailFragment(fragment: TimelineFragment): Fragment
@Binds
@IntoMap
@ -838,6 +838,6 @@ interface FragmentModule {
@Binds
@IntoMap
@FragmentKey(RoomThreadDetailFragment::class)
fun bindRoomThreadDetailFragment(fragment: RoomThreadDetailFragment): Fragment
@FragmentKey(ThreadListFragment::class)
fun bindRoomThreadDetailFragment(fragment: ThreadListFragment): Fragment
}

View file

@ -52,8 +52,7 @@ import im.vector.app.features.home.room.detail.widget.RoomWidgetsBottomSheet
import im.vector.app.features.home.room.filtered.FilteredRoomsActivity
import im.vector.app.features.home.room.list.RoomListModule
import im.vector.app.features.home.room.list.actions.RoomListQuickActionsBottomSheet
import im.vector.app.features.home.room.threads.RoomThreadsActivity
import im.vector.app.features.home.room.threads.detail.RoomThreadDetailActivity
import im.vector.app.features.home.room.threads.ThreadsActivity
import im.vector.app.features.invite.AutoAcceptInvites
import im.vector.app.features.invite.InviteUsersToRoomActivity
import im.vector.app.features.invite.VectorInviteView
@ -176,8 +175,7 @@ interface ScreenComponent {
fun inject(activity: SpaceManageActivity)
fun inject(activity: RoomJoinRuleActivity)
fun inject(activity: SpaceLeaveAdvancedActivity)
fun inject(activity: RoomThreadsActivity)
fun inject(activity: RoomThreadDetailActivity)
fun inject(activity: ThreadsActivity)
/* ==========================================================================================
* BottomSheets

View file

@ -58,7 +58,7 @@ import im.vector.app.features.call.webrtc.WebRtcCallManager
import im.vector.app.features.displayname.getBestName
import im.vector.app.features.home.AvatarRenderer
import im.vector.app.features.home.room.detail.RoomDetailActivity
import im.vector.app.features.home.room.detail.RoomDetailArgs
import im.vector.app.features.home.room.detail.arguments.TimelineArgs
import io.github.hyuwah.draggableviewlib.DraggableView
import io.github.hyuwah.draggableviewlib.setupDraggable
import io.reactivex.android.schedulers.AndroidSchedulers
@ -573,7 +573,7 @@ class VectorCallActivity : VectorBaseActivity<ActivityCallBinding>(), CallContro
private fun returnToChat() {
val roomId = withState(callViewModel) { it.roomId }
val args = RoomDetailArgs(roomId)
val args = TimelineArgs(roomId)
val intent = RoomDetailActivity.newIntent(this, args).apply {
flags = FLAG_ACTIVITY_CLEAR_TOP
}

View file

@ -21,7 +21,7 @@ import im.vector.app.core.platform.VectorBaseActivity
import im.vector.app.features.displayname.getBestName
import im.vector.app.features.home.AvatarRenderer
import im.vector.app.features.home.room.detail.RoomDetailActivity
import im.vector.app.features.home.room.detail.RoomDetailArgs
import im.vector.app.features.home.room.detail.arguments.TimelineArgs
import im.vector.app.features.popup.PopupAlertManager
import im.vector.app.features.popup.VerificationVectorAlert
import org.matrix.android.sdk.api.session.Session
@ -142,7 +142,7 @@ class IncomingVerificationRequestHandler @Inject constructor(
R.drawable.ic_shield_black,
shouldBeDisplayedIn = { activity ->
if (activity is RoomDetailActivity) {
activity.intent?.extras?.getParcelable<RoomDetailArgs>(RoomDetailActivity.EXTRA_ROOM_DETAIL_ARGS)?.let {
activity.intent?.extras?.getParcelable<TimelineArgs>(RoomDetailActivity.EXTRA_ROOM_DETAIL_ARGS)?.let {
it.roomId != pr.roomId
} ?: true
} else true

View file

@ -34,6 +34,7 @@ import im.vector.app.core.platform.ToolbarConfigurable
import im.vector.app.core.platform.VectorBaseActivity
import im.vector.app.databinding.ActivityRoomDetailBinding
import im.vector.app.features.home.room.breadcrumbs.BreadcrumbsFragment
import im.vector.app.features.home.room.detail.arguments.TimelineArgs
import im.vector.app.features.matrixto.MatrixToBottomSheet
import im.vector.app.features.navigation.Navigator
import im.vector.app.features.room.RequireActiveMembershipAction
@ -102,16 +103,16 @@ class RoomDetailActivity :
super.onCreate(savedInstanceState)
supportFragmentManager.registerFragmentLifecycleCallbacks(fragmentLifecycleCallbacks, false)
waitingView = views.waitingView.waitingView
val roomDetailArgs: RoomDetailArgs? = if (intent?.action == ACTION_ROOM_DETAILS_FROM_SHORTCUT) {
RoomDetailArgs(roomId = intent?.extras?.getString(EXTRA_ROOM_ID)!!)
val timelineArgs: TimelineArgs? = if (intent?.action == ACTION_ROOM_DETAILS_FROM_SHORTCUT) {
TimelineArgs(roomId = intent?.extras?.getString(EXTRA_ROOM_ID)!!)
} else {
intent?.extras?.getParcelable(EXTRA_ROOM_DETAIL_ARGS)
}
if (roomDetailArgs == null) return
currentRoomId = roomDetailArgs.roomId
if (timelineArgs == null) return
currentRoomId = timelineArgs.roomId
if (isFirstCreation()) {
replaceFragment(R.id.roomDetailContainer, RoomDetailFragment::class.java, roomDetailArgs)
replaceFragment(R.id.roomDetailContainer, TimelineFragment::class.java, timelineArgs)
replaceFragment(R.id.roomDetailDrawerContainer, BreadcrumbsFragment::class.java)
}
@ -147,7 +148,7 @@ class RoomDetailActivity :
if (currentRoomId != switchToRoom.roomId) {
currentRoomId = switchToRoom.roomId
requireActiveMembershipViewModel.handle(RequireActiveMembershipAction.ChangeRoom(switchToRoom.roomId))
replaceFragment(R.id.roomDetailContainer, RoomDetailFragment::class.java, RoomDetailArgs(switchToRoom.roomId))
replaceFragment(R.id.roomDetailContainer, TimelineFragment::class.java, TimelineArgs(switchToRoom.roomId))
}
}
@ -191,9 +192,9 @@ class RoomDetailActivity :
const val EXTRA_ROOM_ID = "EXTRA_ROOM_ID"
const val ACTION_ROOM_DETAILS_FROM_SHORTCUT = "ROOM_DETAILS_FROM_SHORTCUT"
fun newIntent(context: Context, roomDetailArgs: RoomDetailArgs): Intent {
fun newIntent(context: Context, timelineArgs: TimelineArgs): Intent {
return Intent(context, RoomDetailActivity::class.java).apply {
putExtra(EXTRA_ROOM_DETAIL_ARGS, roomDetailArgs)
putExtra(EXTRA_ROOM_DETAIL_ARGS, timelineArgs)
}
}

View file

@ -153,7 +153,7 @@ class RoomDetailViewModel @AssistedInject constructor(
@JvmStatic
override fun create(viewModelContext: ViewModelContext, state: RoomDetailViewState): RoomDetailViewModel? {
val fragment: RoomDetailFragment = (viewModelContext as FragmentViewModelContext).fragment()
val fragment: TimelineFragment = (viewModelContext as FragmentViewModelContext).fragment()
return fragment.roomDetailViewModelFactory.create(state)
}
@ -668,20 +668,30 @@ class RoomDetailViewModel @AssistedInject constructor(
private fun isIntegrationEnabled() = session.integrationManagerService().isIntegrationEnabled()
fun isMenuItemVisible(@IdRes itemId: Int): Boolean = com.airbnb.mvrx.withState(this) { state ->
if (state.asyncRoomSummary()?.membership != Membership.JOIN) {
return@withState false
}
when (itemId) {
R.id.timeline_setting -> true
R.id.invite -> state.canInvite
R.id.open_matrix_apps -> true
R.id.voice_call -> state.isWebRTCCallOptionAvailable()
R.id.video_call -> state.isWebRTCCallOptionAvailable() || state.jitsiState.confId == null || state.jitsiState.hasJoined
// Show Join conference button only if there is an active conf id not joined. Otherwise fallback to default video disabled. ^
R.id.join_conference -> !state.isWebRTCCallOptionAvailable() && state.jitsiState.confId != null && !state.jitsiState.hasJoined
R.id.search -> true
R.id.dev_tools -> vectorPreferences.developerMode()
else -> false
if (initialState.isThreadTimeline()) {
when (itemId) {
R.id.menu_thread_timeline_more -> true
else -> false
}
} else {
when (itemId) {
R.id.timeline_setting -> true
R.id.invite -> state.canInvite
R.id.open_matrix_apps -> true
R.id.voice_call -> state.isWebRTCCallOptionAvailable()
R.id.video_call -> state.isWebRTCCallOptionAvailable() || state.jitsiState.confId == null || state.jitsiState.hasJoined
// Show Join conference button only if there is an active conf id not joined. Otherwise fallback to default video disabled. ^
R.id.join_conference -> !state.isWebRTCCallOptionAvailable() && state.jitsiState.confId != null && !state.jitsiState.hasJoined
R.id.search -> true
R.id.threads -> true
R.id.dev_tools -> vectorPreferences.developerMode()
else -> false
}
}
}

View file

@ -19,6 +19,7 @@ package im.vector.app.features.home.room.detail
import com.airbnb.mvrx.Async
import com.airbnb.mvrx.MavericksState
import com.airbnb.mvrx.Uninitialized
import im.vector.app.features.home.room.detail.arguments.TimelineArgs
import org.matrix.android.sdk.api.extensions.orFalse
import org.matrix.android.sdk.api.session.events.model.Event
import org.matrix.android.sdk.api.session.initsync.SyncStatusService
@ -69,12 +70,12 @@ data class RoomDetailViewState(
val rootThreadEventId: String? = null
) : MavericksState {
constructor(args: RoomDetailArgs) : this(
constructor(args: TimelineArgs) : this(
roomId = args.roomId,
eventId = args.eventId,
// Also highlight the target event, if any
highlightedEventId = args.eventId,
rootThreadEventId = args.roomThreadDetailArgs?.eventId
rootThreadEventId = args.threadTimelineArgs?.rootThreadEventId
)
fun isWebRTCCallOptionAvailable() = (asyncRoomSummary.invoke()?.joinedMembersCount ?: 0) <= 2
@ -84,4 +85,7 @@ data class RoomDetailViewState(
fun hasActiveJitsiWidget() = activeRoomWidgets()?.any { it.type == WidgetType.Jitsi && it.isActive }.orFalse()
fun isDm() = asyncRoomSummary()?.isDirect == true
fun isThreadTimeline() = rootThreadEventId != null
}

View file

@ -25,7 +25,6 @@ import android.graphics.Typeface
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.os.Parcelable
import android.text.Spannable
import android.text.format.DateUtils
import android.view.HapticFeedbackConstants
@ -49,7 +48,6 @@ import androidx.core.text.toSpannable
import androidx.core.util.Pair
import androidx.core.view.ViewCompat
import androidx.core.view.forEach
import androidx.core.view.isGone
import androidx.core.view.isInvisible
import androidx.core.view.isVisible
import androidx.fragment.app.setFragmentResultListener
@ -134,6 +132,7 @@ import im.vector.app.features.command.Command
import im.vector.app.features.crypto.keysbackup.restore.KeysBackupRestoreActivity
import im.vector.app.features.crypto.verification.VerificationBottomSheet
import im.vector.app.features.home.AvatarRenderer
import im.vector.app.features.home.room.detail.arguments.TimelineArgs
import im.vector.app.features.home.room.detail.composer.SendMode
import im.vector.app.features.home.room.detail.composer.TextComposerAction
import im.vector.app.features.home.room.detail.composer.TextComposerView
@ -162,8 +161,7 @@ import im.vector.app.features.home.room.detail.timeline.url.PreviewUrlRetriever
import im.vector.app.features.home.room.detail.upgrade.MigrateRoomBottomSheet
import im.vector.app.features.home.room.detail.views.RoomDetailLazyLoadedViews
import im.vector.app.features.home.room.detail.widget.RoomWidgetsBottomSheet
import im.vector.app.features.home.room.threads.detail.arguments.RoomThreadDetailArgs
import im.vector.app.features.home.room.threads.detail.RoomThreadDetailActivity
import im.vector.app.features.home.room.threads.arguments.ThreadTimelineArgs
import im.vector.app.features.html.EventHtmlRenderer
import im.vector.app.features.html.PillImageSpan
import im.vector.app.features.html.PillsPostProcessor
@ -188,7 +186,6 @@ import im.vector.app.features.widgets.WidgetArgs
import im.vector.app.features.widgets.WidgetKind
import im.vector.app.features.widgets.permissions.RoomWidgetPermissionBottomSheet
import kotlinx.coroutines.launch
import kotlinx.parcelize.Parcelize
import nl.dionsegijn.konfetti.models.Shape
import nl.dionsegijn.konfetti.models.Size
import org.billcarsonfr.jsonviewer.JSonViewerDialog
@ -224,16 +221,7 @@ import java.util.UUID
import java.util.concurrent.TimeUnit
import javax.inject.Inject
@Parcelize
data class RoomDetailArgs(
val roomId: String,
val eventId: String? = null,
val sharedData: SharedData? = null,
val openShareSpaceForId: String? = null,
val roomThreadDetailArgs: RoomThreadDetailArgs? = null
) : Parcelable
class RoomDetailFragment @Inject constructor(
class TimelineFragment @Inject constructor(
private val session: Session,
private val avatarRenderer: AvatarRenderer,
private val timelineEventController: TimelineEventController,
@ -282,16 +270,16 @@ class RoomDetailFragment @Inject constructor(
private val galleryOrCameraDialogHelper = GalleryOrCameraDialogHelper(this, colorProvider)
private val roomDetailArgs: RoomDetailArgs by args()
private val timelineArgs: TimelineArgs by args()
private val glideRequests by lazy {
GlideApp.with(this)
}
private val pillsPostProcessor by lazy {
pillsPostProcessorFactory.create(roomDetailArgs.roomId)
pillsPostProcessorFactory.create(timelineArgs.roomId)
}
private val autoCompleter: AutoCompleter by lazy {
autoCompleterFactory.create(roomDetailArgs.roomId, isThreadTimeLine())
autoCompleterFactory.create(timelineArgs.roomId, isThreadTimeLine())
}
private val roomDetailViewModel: RoomDetailViewModel by fragmentViewModel()
@ -308,6 +296,8 @@ class RoomDetailFragment @Inject constructor(
override fun getMenuRes() = R.menu.menu_timeline
private lateinit var sharedActionViewModel: MessageSharedActionViewModel
private lateinit var sharedActivityActionViewModel: RoomDetailSharedActionViewModel
private lateinit var knownCallsViewModel: SharedKnownCallsViewModel
private lateinit var layoutManager: LinearLayoutManager
@ -341,10 +331,11 @@ class RoomDetailFragment @Inject constructor(
lifecycle.addObserver(ConferenceEventObserver(vectorBaseActivity, this::onBroadcastJitsiEvent))
super.onViewCreated(view, savedInstanceState)
sharedActionViewModel = activityViewModelProvider.get(MessageSharedActionViewModel::class.java)
sharedActivityActionViewModel = activityViewModelProvider.get(RoomDetailSharedActionViewModel::class.java)
knownCallsViewModel = activityViewModelProvider.get(SharedKnownCallsViewModel::class.java)
attachmentsHelper = AttachmentsHelper(requireContext(), this).register()
callActionsHandler = StartCallActionsHandler(
roomId = roomDetailArgs.roomId,
roomId = timelineArgs.roomId,
fragment = this,
vectorPreferences = vectorPreferences,
roomDetailViewModel = roomDetailViewModel,
@ -355,11 +346,7 @@ class RoomDetailFragment @Inject constructor(
)
keyboardStateUtils = KeyboardStateUtils(requireActivity())
lazyLoadedViews.bind(views)
if (isThreadTimeLine()) {
views.roomToolbar.isGone = true
} else {
setupToolbar(views.roomToolbar)
}
setupToolbar(views.roomToolbar)
setupRecyclerView()
setupComposer()
setupNotificationView()
@ -370,8 +357,8 @@ class RoomDetailFragment @Inject constructor(
setupRemoveJitsiWidgetView()
setupVoiceMessageView()
views.roomToolbarContentView.debouncedClicks {
navigator.openRoomProfile(requireActivity(), roomDetailArgs.roomId)
views.includeRoomToolbar.roomToolbarContentView.debouncedClicks {
navigator.openRoomProfile(requireActivity(), timelineArgs.roomId)
}
sharedActionViewModel
@ -456,7 +443,7 @@ class RoomDetailFragment @Inject constructor(
RoomDetailViewEvents.HideWaitingView -> vectorBaseActivity.hideWaitingView()
is RoomDetailViewEvents.RequestNativeWidgetPermission -> requestNativeWidgetPermission(it)
is RoomDetailViewEvents.OpenRoom -> handleOpenRoom(it)
RoomDetailViewEvents.OpenInvitePeople -> navigator.openInviteUsersToRoom(requireContext(), roomDetailArgs.roomId)
RoomDetailViewEvents.OpenInvitePeople -> navigator.openInviteUsersToRoom(requireContext(), timelineArgs.roomId)
RoomDetailViewEvents.OpenSetRoomAvatarDialog -> galleryOrCameraDialogHelper.show()
RoomDetailViewEvents.OpenRoomSettings -> handleOpenRoomSettings()
is RoomDetailViewEvents.ShowRoomAvatarFullScreen -> it.matrixItem?.let { item ->
@ -532,7 +519,7 @@ class RoomDetailFragment @Inject constructor(
private fun handleShowRoomUpgradeDialog(roomDetailViewEvents: TextComposerViewEvents.ShowRoomUpgradeDialog) {
val tag = MigrateRoomBottomSheet::javaClass.name
MigrateRoomBottomSheet.newInstance(roomDetailArgs.roomId, roomDetailViewEvents.newVersion)
MigrateRoomBottomSheet.newInstance(timelineArgs.roomId, roomDetailViewEvents.newVersion)
.show(parentFragmentManager, tag)
}
@ -578,7 +565,7 @@ class RoomDetailFragment @Inject constructor(
private fun handleOpenRoomSettings() {
navigator.openRoomProfile(
requireContext(),
roomDetailArgs.roomId,
timelineArgs.roomId,
RoomProfileActivity.EXTRA_DIRECT_ACCESS_ROOM_SETTINGS
)
}
@ -600,7 +587,7 @@ class RoomDetailFragment @Inject constructor(
WidgetArgs(
baseUrl = it.domain,
kind = WidgetKind.ROOM,
roomId = roomDetailArgs.roomId,
roomId = timelineArgs.roomId,
widgetId = it.widget.widgetId
)
).apply {
@ -626,7 +613,7 @@ class RoomDetailFragment @Inject constructor(
navigator.openIntegrationManager(
context = requireContext(),
activityResultLauncher = integrationManagerActivityResultLauncher,
roomId = roomDetailArgs.roomId,
roomId = timelineArgs.roomId,
integId = null,
screen = screen
)
@ -717,11 +704,11 @@ class RoomDetailFragment @Inject constructor(
}
private fun joinJitsiRoom(jitsiWidget: Widget, enableVideo: Boolean) {
navigator.openRoomWidget(requireContext(), roomDetailArgs.roomId, jitsiWidget, mapOf(JitsiCallViewModel.ENABLE_VIDEO_OPTION to enableVideo))
navigator.openRoomWidget(requireContext(), timelineArgs.roomId, jitsiWidget, mapOf(JitsiCallViewModel.ENABLE_VIDEO_OPTION to enableVideo))
}
private fun openStickerPicker(event: RoomDetailViewEvents.OpenStickerPicker) {
navigator.openStickerPicker(requireContext(), stickerActivityResultLauncher, roomDetailArgs.roomId, event.widget)
navigator.openStickerPicker(requireContext(), stickerActivityResultLauncher, timelineArgs.roomId, event.widget)
}
private fun startOpenFileIntent(action: RoomDetailViewEvents.OpenFile) {
@ -795,7 +782,7 @@ class RoomDetailFragment @Inject constructor(
}
private fun handleShareData() {
when (val sharedData = roomDetailArgs.sharedData) {
when (val sharedData = timelineArgs.sharedData) {
is SharedData.Text -> {
textComposerViewModel.handle(TextComposerAction.EnterRegularMode(sharedData.text, fromSharing = true))
}
@ -808,7 +795,7 @@ class RoomDetailFragment @Inject constructor(
}
private fun handleSpaceShare() {
roomDetailArgs.openShareSpaceForId?.let { spaceId ->
timelineArgs.openShareSpaceForId?.let { spaceId ->
ShareSpaceBottomSheet.show(childFragmentManager, spaceId, true)
view?.post {
handleChatEffect(ChatEffect.CONFETTI)
@ -909,7 +896,6 @@ class RoomDetailFragment @Inject constructor(
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
super.onCreateOptionsMenu(menu, inflater)
if (isThreadTimeLine()) return
// We use a custom layout for this menu item, so we need to set a ClickListener
menu.findItem(R.id.open_matrix_apps)?.let { menuItem ->
menuItem.actionView.setOnClickListener {
@ -923,15 +909,11 @@ class RoomDetailFragment @Inject constructor(
}
override fun onPrepareOptionsMenu(menu: Menu) {
if (isThreadTimeLine()) {
menu.forEach {
it.isVisible = false
}
return
}
menu.forEach {
it.isVisible = roomDetailViewModel.isMenuItemVisible(it.itemId)
}
withState(roomDetailViewModel) { state ->
// Set the visual state of the call buttons (voice/video) to enabled/disabled according to user permissions
val hasCallInRoom = callManager.getCallsByRoomId(state.roomId).isNotEmpty() || state.jitsiState.hasJoined
@ -968,41 +950,72 @@ class RoomDetailFragment @Inject constructor(
override fun onOptionsItemSelected(item: MenuItem): Boolean {
return when (item.itemId) {
R.id.invite -> {
navigator.openInviteUsersToRoom(requireActivity(), roomDetailArgs.roomId)
R.id.invite -> {
navigator.openInviteUsersToRoom(requireActivity(), timelineArgs.roomId)
true
}
R.id.timeline_setting -> {
navigator.openRoomProfile(requireActivity(), roomDetailArgs.roomId)
R.id.timeline_setting -> {
navigator.openRoomProfile(requireActivity(), timelineArgs.roomId)
true
}
R.id.open_matrix_apps -> {
R.id.open_matrix_apps -> {
roomDetailViewModel.handle(RoomDetailAction.ManageIntegrations)
true
}
R.id.voice_call -> {
R.id.voice_call -> {
callActionsHandler.onVoiceCallClicked()
true
}
R.id.video_call -> {
R.id.video_call -> {
callActionsHandler.onVideoCallClicked()
true
}
R.id.search -> {
R.id.threads -> {
requireActivity().toast("View All Threads")
true
}
R.id.search -> {
handleSearchAction()
true
}
R.id.dev_tools -> {
navigator.openDevTools(requireContext(), roomDetailArgs.roomId)
R.id.dev_tools -> {
navigator.openDevTools(requireContext(), timelineArgs.roomId)
true
}
else -> super.onOptionsItemSelected(item)
R.id.menu_thread_timeline_copy_link -> {
requireActivity().toast("menu_thread_timeline_copy_link")
true
}
R.id.menu_thread_timeline_view_in_room -> {
handleViewInRoomAction()
true
}
R.id.menu_thread_timeline_share -> {
requireActivity().toast("menu_thread_timeline_share")
true
}
else -> super.onOptionsItemSelected(item)
}
}
/**
* View and highlight the original root thread message in the main timeline
*/
private fun handleViewInRoomAction(){
getRootThreadEventId()?.let {
val newRoom = timelineArgs.copy(threadTimelineArgs = null,eventId = it)
context?.let{ con ->
val int = RoomDetailActivity.newIntent(con, newRoom)
int.flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_NEW_TASK
con.startActivity(int)
}
}
}
private fun handleSearchAction() {
if (session.getRoom(roomDetailArgs.roomId)?.isEncrypted() == false) {
navigator.openSearch(requireContext(), roomDetailArgs.roomId)
if (session.getRoom(timelineArgs.roomId)?.isEncrypted() == false) {
navigator.openSearch(requireContext(), timelineArgs.roomId)
} else {
showDialogWithMessage(getString(R.string.search_is_not_supported_in_e2e_room))
}
@ -1080,7 +1093,7 @@ class RoomDetailFragment @Inject constructor(
override fun onResume() {
super.onResume()
notificationDrawerManager.setCurrentRoom(roomDetailArgs.roomId)
notificationDrawerManager.setCurrentRoom(timelineArgs.roomId)
roomDetailPendingActionStore.data?.let { handlePendingAction(it) }
roomDetailPendingActionStore.data = null
@ -1322,7 +1335,7 @@ class RoomDetailFragment @Inject constructor(
views.composerLayout.callback = object : TextComposerView.Callback {
override fun onAddAttachment() {
if (!::attachmentTypeSelector.isInitialized) {
attachmentTypeSelector = AttachmentTypeSelectorView(vectorBaseActivity, vectorBaseActivity.layoutInflater, this@RoomDetailFragment)
attachmentTypeSelector = AttachmentTypeSelectorView(vectorBaseActivity, vectorBaseActivity.layoutInflater, this@TimelineFragment)
}
attachmentTypeSelector.show(views.composerLayout.views.attachmentButton, keyboardStateUtils.isKeyboardShowing)
}
@ -1420,7 +1433,7 @@ class RoomDetailFragment @Inject constructor(
} else if (summary?.membership == Membership.INVITE && inviter != null) {
views.hideComposerViews()
lazyLoadedViews.inviteView(true)?.apply {
callback = this@RoomDetailFragment
callback = this@TimelineFragment
isVisible = true
render(inviter, VectorInviteView.Mode.LARGE, mainState.changeMembershipState)
setOnClickListener { }
@ -1437,23 +1450,35 @@ class RoomDetailFragment @Inject constructor(
}
private fun renderToolbar(roomSummary: RoomSummary?, typingMessage: String?) {
if (roomSummary == null) {
views.roomToolbarContentView.isClickable = false
if (!isThreadTimeLine()) {
views.includeRoomToolbar.roomToolbarContentView.isVisible = true
views.includeThreadToolbar.roomToolbarThreadConstraintLayout.isVisible = false
if (roomSummary == null) {
views.includeRoomToolbar.roomToolbarContentView.isClickable = false
} else {
views.includeRoomToolbar.roomToolbarContentView.isClickable = roomSummary.membership == Membership.JOIN
views.includeRoomToolbar.roomToolbarTitleView.text = roomSummary.displayName
avatarRenderer.render(roomSummary.toMatrixItem(), views.includeRoomToolbar.roomToolbarAvatarImageView)
renderSubTitle(typingMessage, roomSummary.topic)
views.includeRoomToolbar.roomToolbarDecorationImageView.render(roomSummary.roomEncryptionTrustLevel)
views.includeRoomToolbar.roomToolbarPresenceImageView.render(roomSummary.isDirect, roomSummary.directUserPresence)
views.includeRoomToolbar.roomToolbarPublicImageView.isVisible = roomSummary.isPublic && !roomSummary.isDirect
}
} else {
views.roomToolbarContentView.isClickable = roomSummary.membership == Membership.JOIN
views.roomToolbarTitleView.text = roomSummary.displayName
avatarRenderer.render(roomSummary.toMatrixItem(), views.roomToolbarAvatarImageView)
renderSubTitle(typingMessage, roomSummary.topic)
views.roomToolbarDecorationImageView.render(roomSummary.roomEncryptionTrustLevel)
views.roomToolbarPresenceImageView.render(roomSummary.isDirect, roomSummary.directUserPresence)
views.roomToolbarPublicImageView.isVisible = roomSummary.isPublic && !roomSummary.isDirect
views.includeRoomToolbar.roomToolbarContentView.isVisible = false
views.includeThreadToolbar.roomToolbarThreadConstraintLayout.isVisible = true
timelineArgs.threadTimelineArgs?.let {
val matrixItem = MatrixItem.RoomItem(it.roomId, it.displayName, it.avatarUrl)
avatarRenderer.render(matrixItem, views.includeThreadToolbar.roomToolbarThreadImageView)
views.includeThreadToolbar.roomToolbarThreadSubtitleTextView.text = it.displayName
}
}
}
private fun renderSubTitle(typingMessage: String?, topic: String) {
// TODO Temporary place to put typing data
val subtitle = typingMessage?.takeIf { it.isNotBlank() } ?: topic
views.roomToolbarSubtitleView.apply {
views.includeRoomToolbar.roomToolbarSubtitleView.apply {
setTextOrHide(subtitle)
if (typingMessage.isNullOrBlank()) {
setTextColor(colorProvider.getColorFromAttribute(R.attr.vctr_content_primary))
@ -1592,14 +1617,14 @@ class RoomDetailFragment @Inject constructor(
is RoomDetailAction.RequestVerification -> {
Timber.v("## SAS RequestVerification action")
VerificationBottomSheet.withArgs(
roomDetailArgs.roomId,
timelineArgs.roomId,
data.userId
).show(parentFragmentManager, "REQ")
}
is RoomDetailAction.AcceptVerificationRequest -> {
Timber.v("## SAS AcceptVerificationRequest action")
VerificationBottomSheet.withArgs(
roomDetailArgs.roomId,
timelineArgs.roomId,
data.otherUserId,
data.transactionId
).show(parentFragmentManager, "REQ")
@ -1609,7 +1634,7 @@ class RoomDetailFragment @Inject constructor(
VerificationBottomSheet().apply {
arguments = Bundle().apply {
putParcelable(Mavericks.KEY_ARG, VerificationBottomSheet.VerificationArgs(
otherUserId, data.transactionId, roomId = roomDetailArgs.roomId))
otherUserId, data.transactionId, roomId = timelineArgs.roomId))
}
}.show(parentFragmentManager, "REQ")
}
@ -1624,7 +1649,7 @@ class RoomDetailFragment @Inject constructor(
.launch(requireActivity(), url, object : NavigationInterceptor {
override fun navToRoom(roomId: String?, eventId: String?, deepLink: Uri?): Boolean {
// Same room?
if (roomId == roomDetailArgs.roomId) {
if (roomId == timelineArgs.roomId) {
// Navigation to same room
if (eventId == null) {
showSnackWithMessage(getString(R.string.navigate_to_room_when_already_in_the_room))
@ -1691,7 +1716,7 @@ class RoomDetailFragment @Inject constructor(
override fun onImageMessageClicked(messageImageContent: MessageImageInfoContent, mediaData: ImageContentRenderer.Data, view: View) {
navigator.openMediaViewer(
activity = requireActivity(),
roomId = roomDetailArgs.roomId,
roomId = timelineArgs.roomId,
mediaData = mediaData,
view = view
) { pairs ->
@ -1703,7 +1728,7 @@ class RoomDetailFragment @Inject constructor(
override fun onVideoMessageClicked(messageVideoContent: MessageVideoContent, mediaData: VideoContentRenderer.Data, view: View) {
navigator.openMediaViewer(
activity = requireActivity(),
roomId = roomDetailArgs.roomId,
roomId = timelineArgs.roomId,
mediaData = mediaData,
view = view
) { pairs ->
@ -1738,7 +1763,7 @@ class RoomDetailFragment @Inject constructor(
roomDetailViewModel.handle(RoomDetailAction.LoadMoreTimelineEvents(direction))
}
override fun onEventCellClicked(informationData: MessageInformationData, messageContent: Any?, view: View) {
override fun onEventCellClicked(informationData: MessageInformationData, messageContent: Any?, view: View, isRootThreadEvent: Boolean) {
when (messageContent) {
is MessageVerificationRequestContent -> {
roomDetailViewModel.handle(RoomDetailAction.ResumeVerification(informationData.eventId, null))
@ -1751,11 +1776,14 @@ class RoomDetailFragment @Inject constructor(
roomDetailViewModel.handle(RoomDetailAction.TapOnFailedToDecrypt(informationData.eventId))
}
}
if (isRootThreadEvent) {
navigateToThreadTimeline(informationData.eventId)
}
}
override fun onEventLongClicked(informationData: MessageInformationData, messageContent: Any?, view: View): Boolean {
view.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS)
val roomId = roomDetailArgs.roomId
val roomId = timelineArgs.roomId
this.view?.hideKeyboard()
MessageActionsBottomSheet
@ -1786,7 +1814,7 @@ class RoomDetailFragment @Inject constructor(
}
private fun openRoomMemberProfile(userId: String) {
navigator.openRoomMemberProfile(userId = userId, roomId = roomDetailArgs.roomId, context = requireActivity())
navigator.openRoomMemberProfile(userId = userId, roomId = timelineArgs.roomId, context = requireActivity())
}
override fun onMemberNameClicked(informationData: MessageInformationData) {
@ -1804,12 +1832,12 @@ class RoomDetailFragment @Inject constructor(
}
override fun onLongClickOnReactionPill(informationData: MessageInformationData, reaction: String) {
ViewReactionsBottomSheet.newInstance(roomDetailArgs.roomId, informationData)
ViewReactionsBottomSheet.newInstance(timelineArgs.roomId, informationData)
.show(requireActivity().supportFragmentManager, "DISPLAY_REACTIONS")
}
override fun onEditedDecorationClicked(informationData: MessageInformationData) {
ViewEditHistoryBottomSheet.newInstance(roomDetailArgs.roomId, informationData)
ViewEditHistoryBottomSheet.newInstance(timelineArgs.roomId, informationData)
.show(requireActivity().supportFragmentManager, "DISPLAY_EDITS")
}
@ -1921,7 +1949,7 @@ class RoomDetailFragment @Inject constructor(
emojiActivityResultLauncher.launch(EmojiReactionPickerActivity.intent(requireContext(), action.eventId))
}
is EventSharedAction.ViewReactions -> {
ViewReactionsBottomSheet.newInstance(roomDetailArgs.roomId, action.messageInformationData)
ViewReactionsBottomSheet.newInstance(timelineArgs.roomId, action.messageInformationData)
.show(requireActivity().supportFragmentManager, "DISPLAY_REACTIONS")
}
is EventSharedAction.Copy -> {
@ -1978,20 +2006,13 @@ class RoomDetailFragment @Inject constructor(
}
is EventSharedAction.ReplyInThread -> {
if (!views.voiceMessageRecorderView.isActive()) {
context?.let {
val roomThreadDetailArgs = RoomThreadDetailArgs(
roomId = roomDetailArgs.roomId,
displayName = roomDetailViewModel.getRoomSummary()?.displayName,
avatarUrl = roomDetailViewModel.getRoomSummary()?.avatarUrl,
eventId = action.eventId)
startActivity(RoomThreadDetailActivity.newIntent(it, roomThreadDetailArgs))
}
navigateToThreadTimeline(action.eventId)
} else {
requireActivity().toast(R.string.error_voice_message_cannot_reply_or_edit)
}
}
is EventSharedAction.CopyPermalink -> {
val permalink = session.permalinkService().createPermalink(roomDetailArgs.roomId, action.eventId)
val permalink = session.permalinkService().createPermalink(timelineArgs.roomId, action.eventId)
copyToClipboard(requireContext(), permalink, false)
showSnackWithMessage(getString(R.string.copied_to_clipboard))
}
@ -2114,15 +2135,31 @@ class RoomDetailFragment @Inject constructor(
.show()
}
/**
* Navigate to Threads timeline for the specified threadRootEventId
* using the RoomThreadDetailActivity
*/
private fun navigateToThreadTimeline(rootThreadEventId: String) {
context?.let {
val roomThreadDetailArgs = ThreadTimelineArgs(
roomId = timelineArgs.roomId,
displayName = roomDetailViewModel.getRoomSummary()?.displayName,
avatarUrl = roomDetailViewModel.getRoomSummary()?.avatarUrl,
rootThreadEventId = rootThreadEventId)
navigator.openThread(it, roomThreadDetailArgs)
}
}
// VectorInviteView.Callback
override fun onAcceptInvite() {
notificationDrawerManager.clearMemberShipNotificationForRoom(roomDetailArgs.roomId)
notificationDrawerManager.clearMemberShipNotificationForRoom(timelineArgs.roomId)
roomDetailViewModel.handle(RoomDetailAction.AcceptInvite)
}
override fun onRejectInvite() {
notificationDrawerManager.clearMemberShipNotificationForRoom(roomDetailArgs.roomId)
notificationDrawerManager.clearMemberShipNotificationForRoom(timelineArgs.roomId)
roomDetailViewModel.handle(RoomDetailAction.RejectInvite)
}
@ -2221,6 +2258,13 @@ class RoomDetailFragment @Inject constructor(
}
}
private fun isThreadTimeLine(): Boolean = roomDetailArgs.roomThreadDetailArgs != null
fun getRootThreadEventId(): String? = roomDetailArgs.roomThreadDetailArgs?.eventId
/**
* Returns true if the current room is a Thread room, false otherwise
*/
private fun isThreadTimeLine(): Boolean = timelineArgs.threadTimelineArgs?.rootThreadEventId != null
/**
* Returns the root thread event if we are in a thread room, otherwise returns null
*/
fun getRootThreadEventId(): String? = timelineArgs.threadTimelineArgs?.rootThreadEventId
}

View file

@ -0,0 +1,31 @@
/*
* Copyright (c) 2021 New Vector Ltd
*
* 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
*
* http://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.
*/
package im.vector.app.features.home.room.detail.arguments
import android.os.Parcelable
import im.vector.app.features.home.room.threads.arguments.ThreadTimelineArgs
import im.vector.app.features.share.SharedData
import kotlinx.parcelize.Parcelize
@Parcelize
data class TimelineArgs(
val roomId: String,
val eventId: String? = null,
val sharedData: SharedData? = null,
val openShareSpaceForId: String? = null,
val threadTimelineArgs: ThreadTimelineArgs? = null
) : Parcelable

View file

@ -29,7 +29,7 @@ import im.vector.app.core.resources.StringProvider
import im.vector.app.features.command.CommandParser
import im.vector.app.features.command.ParsedCommand
import im.vector.app.features.home.room.detail.ChatEffect
import im.vector.app.features.home.room.detail.RoomDetailFragment
import im.vector.app.features.home.room.detail.TimelineFragment
import im.vector.app.features.home.room.detail.composer.rainbow.RainbowGenerator
import im.vector.app.features.home.room.detail.toMessageType
import im.vector.app.features.powerlevel.PowerLevelsFlowFactory
@ -42,7 +42,6 @@ import org.commonmark.renderer.html.HtmlRenderer
import org.matrix.android.sdk.api.query.QueryStringValue
import org.matrix.android.sdk.api.session.Session
import org.matrix.android.sdk.api.session.events.model.EventType
import org.matrix.android.sdk.api.session.events.model.getRootThreadEventId
import org.matrix.android.sdk.api.session.events.model.toContent
import org.matrix.android.sdk.api.session.events.model.toModel
import org.matrix.android.sdk.api.session.room.model.PowerLevelsContent
@ -772,7 +771,7 @@ class TextComposerViewModel @AssistedInject constructor(
@JvmStatic
override fun create(viewModelContext: ViewModelContext, state: TextComposerViewState): TextComposerViewModel {
val fragment: RoomDetailFragment = (viewModelContext as FragmentViewModelContext).fragment()
val fragment: TimelineFragment = (viewModelContext as FragmentViewModelContext).fragment()
return fragment.textComposerViewModelFactory.create(state)
}
}

View file

@ -17,7 +17,7 @@
package im.vector.app.features.home.room.detail.composer
import com.airbnb.mvrx.MavericksState
import im.vector.app.features.home.room.detail.RoomDetailArgs
import im.vector.app.features.home.room.detail.arguments.TimelineArgs
import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
/**
@ -53,9 +53,9 @@ data class TextComposerViewState(
val isComposerVisible: Boolean
get() = canSendMessage && !isVoiceRecording
constructor(args: RoomDetailArgs) : this(
constructor(args: TimelineArgs) : this(
roomId = args.roomId,
rootThreadEventId = args.roomThreadDetailArgs?.eventId)
rootThreadEventId = args.threadTimelineArgs?.rootThreadEventId)
fun isInThreadTimeline(): Boolean = rootThreadEventId != null
}

View file

@ -140,7 +140,7 @@ class TimelineEventController @Inject constructor(private val dateFormatter: Vec
}
interface BaseCallback {
fun onEventCellClicked(informationData: MessageInformationData, messageContent: Any?, view: View)
fun onEventCellClicked(informationData: MessageInformationData, messageContent: Any?, view: View, isRootThreadEvent: Boolean)
fun onEventLongClicked(informationData: MessageInformationData, messageContent: Any?, view: View): Boolean
}

View file

@ -43,7 +43,7 @@ class MessageItemAttributesFactory @Inject constructor(
callback?.onEventLongClicked(informationData, messageContent, view) ?: false
},
itemClickListener = { view ->
callback?.onEventCellClicked(informationData, messageContent, view)
callback?.onEventCellClicked(informationData, messageContent, view, threadDetails?.isRootThread ?: false)
},
memberClickListener = {
callback?.onMemberNameClicked(informationData)

View file

@ -1,66 +0,0 @@
/*
* Copyright 2021 New Vector Ltd
*
* 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
*
* http://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.
*/
package im.vector.app.features.home.room.threads
import android.content.Context
import android.content.Intent
import android.os.Bundle
import androidx.appcompat.widget.SearchView
import im.vector.app.R
import im.vector.app.core.di.ScreenComponent
import im.vector.app.core.extensions.replaceFragment
import im.vector.app.core.platform.VectorBaseActivity
import im.vector.app.databinding.ActivityFilteredRoomsBinding
import im.vector.app.databinding.ActivityRoomThreadsBinding
import im.vector.app.features.home.RoomListDisplayMode
import im.vector.app.features.home.room.list.RoomListFragment
import im.vector.app.features.home.room.list.RoomListParams
class RoomThreadsActivity : VectorBaseActivity<ActivityRoomThreadsBinding>() {
// private val roomListFragment: RoomListFragment?
// get() {
// return supportFragmentManager.findFragmentByTag(FRAGMENT_TAG) as? RoomListFragment
// }
override fun getBinding() = ActivityRoomThreadsBinding.inflate(layoutInflater)
override fun getCoordinatorLayout() = views.coordinatorLayout
override fun injectWith(injector: ScreenComponent) {
injector.inject(this)
}
override fun getMenuRes() = R.menu.menu_room_threads
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
configureToolbar(views.roomThreadsToolbar)
// if (isFirstCreation()) {
// val params = RoomListParams(RoomListDisplayMode.FILTERED)
// replaceFragment(R.id.filteredRoomsFragmentContainer, RoomListFragment::class.java, params, FRAGMENT_TAG)
// }
}
companion object {
private const val FRAGMENT_TAG = "RoomListFragment"
fun newIntent(context: Context): Intent {
return Intent(context, RoomThreadsActivity::class.java)
}
}
}

View file

@ -0,0 +1,132 @@
/*
* Copyright (c) 2021 New Vector Ltd
*
* 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
*
* http://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.
*/
package im.vector.app.features.home.room.threads
import android.content.Context
import android.content.Intent
import android.os.Bundle
import com.google.android.material.appbar.MaterialToolbar
import im.vector.app.R
import im.vector.app.core.di.ScreenComponent
import im.vector.app.core.extensions.replaceFragment
import im.vector.app.core.platform.ToolbarConfigurable
import im.vector.app.core.platform.VectorBaseActivity
import im.vector.app.core.platform.VectorViewModelAction
import im.vector.app.databinding.ActivityThreadsBinding
import im.vector.app.features.home.AvatarRenderer
import im.vector.app.features.home.room.detail.arguments.TimelineArgs
import im.vector.app.features.home.room.detail.TimelineFragment
import im.vector.app.features.home.room.threads.arguments.ThreadListArgs
import im.vector.app.features.home.room.threads.arguments.ThreadTimelineArgs
import im.vector.app.features.home.room.threads.detail.ThreadListFragment
import javax.inject.Inject
class ThreadsActivity : VectorBaseActivity<ActivityThreadsBinding>(), ToolbarConfigurable {
@Inject
lateinit var avatarRenderer: AvatarRenderer
// private val roomThreadDetailFragment: RoomThreadDetailFragment?
// get() {
// return supportFragmentManager.findFragmentByTag(FRAGMENT_TAG) as? RoomThreadDetailFragment
// }
override fun getBinding() = ActivityThreadsBinding.inflate(layoutInflater)
override fun getCoordinatorLayout() = views.coordinatorLayout
override fun injectWith(injector: ScreenComponent) {
injector.inject(this)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
initFragment()
}
private fun initFragment() {
if (isFirstCreation()) {
when (val fragment = fragmentToNavigate()) {
is DisplayFragment.ThreadList -> {
initThreadListFragment(fragment.threadListArgs)
}
is DisplayFragment.ThreadTimeLine -> {
initThreadTimelineFragment(fragment.threadTimelineArgs)
}
is DisplayFragment.ErrorFragment -> {
finish()
}
}
}
}
private fun initThreadListFragment(threadListArgs: ThreadListArgs) {
replaceFragment(
R.id.threadsActivityFragmentContainer,
ThreadListFragment::class.java,
threadListArgs)
}
private fun initThreadTimelineFragment(threadTimelineArgs: ThreadTimelineArgs) =
replaceFragment(
R.id.threadsActivityFragmentContainer,
TimelineFragment::class.java,
TimelineArgs(
roomId = threadTimelineArgs.roomId,
threadTimelineArgs = threadTimelineArgs
))
override fun configure(toolbar: MaterialToolbar) {
configureToolbar(toolbar)
}
/**
* Determine in witch fragment we should navigate
*/
private fun fragmentToNavigate(): DisplayFragment {
getThreadTimelineArgs()?.let {
return DisplayFragment.ThreadTimeLine(it)
}
getThreadListArgs()?.let {
return DisplayFragment.ThreadList(it)
}
return DisplayFragment.ErrorFragment
}
private fun getThreadTimelineArgs(): ThreadTimelineArgs? = intent?.extras?.getParcelable(THREAD_TIMELINE_ARGS)
private fun getThreadListArgs(): ThreadListArgs? = intent?.extras?.getParcelable(THREAD_LIST_ARGS)
companion object {
// private val FRAGMENT_TAG = RoomThreadDetailFragment::class.simpleName
const val THREAD_TIMELINE_ARGS = "THREAD_TIMELINE_ARGS"
const val THREAD_LIST_ARGS = "THREAD_LIST_ARGS"
fun newIntent(context: Context, threadTimelineArgs: ThreadTimelineArgs?, threadListArgs: ThreadListArgs?): Intent {
return Intent(context, ThreadsActivity::class.java).apply {
putExtra(THREAD_TIMELINE_ARGS, threadTimelineArgs)
putExtra(THREAD_LIST_ARGS, threadListArgs)
}
}
}
sealed class DisplayFragment {
data class ThreadList(val threadListArgs: ThreadListArgs) : DisplayFragment()
data class ThreadTimeLine(val threadTimelineArgs: ThreadTimelineArgs) : DisplayFragment()
object ErrorFragment : DisplayFragment()
}
}

View file

@ -0,0 +1,25 @@
/*
* Copyright (c) 2021 New Vector Ltd
*
* 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
*
* http://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.
*/
package im.vector.app.features.home.room.threads.arguments
import android.os.Parcelable
import kotlinx.parcelize.Parcelize
@Parcelize
data class ThreadListArgs(
val roomId: String
) : Parcelable

View file

@ -14,15 +14,15 @@
* limitations under the License.
*/
package im.vector.app.features.home.room.threads.detail.arguments
package im.vector.app.features.home.room.threads.arguments
import android.os.Parcelable
import kotlinx.parcelize.Parcelize
@Parcelize
data class RoomThreadDetailArgs(
data class ThreadTimelineArgs(
val roomId: String,
val displayName: String?,
val avatarUrl: String?,
val eventId: String? = null,
val rootThreadEventId: String? = null
) : Parcelable

View file

@ -1,95 +0,0 @@
/*
* Copyright 2021 New Vector Ltd
*
* 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
*
* http://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.
*/
package im.vector.app.features.home.room.threads.detail
import android.content.Context
import android.content.Intent
import android.os.Bundle
import im.vector.app.R
import im.vector.app.core.di.ScreenComponent
import im.vector.app.core.extensions.replaceFragment
import im.vector.app.core.platform.VectorBaseActivity
import im.vector.app.databinding.ActivityRoomThreadDetailBinding
import im.vector.app.features.home.AvatarRenderer
import im.vector.app.features.home.room.detail.RoomDetailArgs
import im.vector.app.features.home.room.detail.RoomDetailFragment
import im.vector.app.features.home.room.threads.detail.arguments.RoomThreadDetailArgs
import org.matrix.android.sdk.api.util.MatrixItem
import javax.inject.Inject
class RoomThreadDetailActivity : VectorBaseActivity<ActivityRoomThreadDetailBinding>() {
@Inject
lateinit var avatarRenderer: AvatarRenderer
// private val roomThreadDetailFragment: RoomThreadDetailFragment?
// get() {
// return supportFragmentManager.findFragmentByTag(FRAGMENT_TAG) as? RoomThreadDetailFragment
// }
override fun getBinding() = ActivityRoomThreadDetailBinding.inflate(layoutInflater)
override fun getCoordinatorLayout() = views.coordinatorLayout
override fun injectWith(injector: ScreenComponent) {
injector.inject(this)
}
override fun getMenuRes() = R.menu.menu_room_threads
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
initToolbar()
initFragment()
}
private fun initToolbar() {
configureToolbar(views.roomThreadDetailToolbar)
getRoomThreadDetailArgs()?.let {
val matrixItem = MatrixItem.RoomItem(it.roomId, it.displayName, it.avatarUrl)
avatarRenderer.render(matrixItem, views.roomThreadDetailToolbarImageView)
}
}
private fun initFragment() {
if (isFirstCreation()) {
getRoomThreadDetailArgs()?.let {
replaceFragment(
R.id.roomThreadDetailFragmentContainer,
RoomDetailFragment::class.java,
RoomDetailArgs(
roomId = it.roomId,
roomThreadDetailArgs = it
))
}
// replaceFragment(R.id.roomThreadDetailFragmentContainer, RoomThreadDetailFragment::class.java, getRoomThreadDetailArgs(), FRAGMENT_TAG)
}
}
private fun getRoomThreadDetailArgs(): RoomThreadDetailArgs? = intent?.extras?.getParcelable(ROOM_THREAD_DETAIL_ARGS)
companion object {
private val FRAGMENT_TAG = RoomThreadDetailFragment::class.simpleName
const val ROOM_THREAD_DETAIL_ARGS = "ROOM_THREAD_DETAIL_ARGS"
fun newIntent(context: Context, roomThreadDetailArgs: RoomThreadDetailArgs): Intent {
return Intent(context, RoomThreadDetailActivity::class.java).apply {
putExtra(ROOM_THREAD_DETAIL_ARGS, roomThreadDetailArgs)
}
}
}
}

View file

@ -16,34 +16,31 @@
package im.vector.app.features.home.room.threads.detail
import android.annotation.SuppressLint
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.core.view.isVisible
import com.airbnb.mvrx.args
import im.vector.app.core.platform.VectorBaseFragment
import im.vector.app.databinding.FragmentRoomThreadDetailBinding
import im.vector.app.features.home.room.threads.detail.arguments.RoomThreadDetailArgs
import im.vector.app.databinding.FragmentThreadListBinding
import im.vector.app.features.home.room.threads.arguments.ThreadTimelineArgs
import org.matrix.android.sdk.api.session.Session
import javax.inject.Inject
class RoomThreadDetailFragment @Inject constructor(
class ThreadListFragment @Inject constructor(
private val session: Session
) : VectorBaseFragment<FragmentRoomThreadDetailBinding>() {
) : VectorBaseFragment<FragmentThreadListBinding>() {
private val roomThreadDetailArgs: RoomThreadDetailArgs by args()
private val threadTimelineArgs: ThreadTimelineArgs by args()
override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentRoomThreadDetailBinding {
return FragmentRoomThreadDetailBinding.inflate(inflater, container, false)
override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentThreadListBinding {
return FragmentThreadListBinding.inflate(inflater, container, false)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
}
@SuppressLint("SetTextI18n")
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
initTextComposer()
@ -59,7 +56,7 @@ class RoomThreadDetailFragment @Inject constructor(
}
private fun initTextComposer(){
views.roomThreadDetailTextComposerView.views.sendButton.isVisible = true
// views.roomThreadDetailTextComposerView.views.sendButton.isVisible = true
}
}

View file

@ -49,10 +49,13 @@ import im.vector.app.features.crypto.verification.VerificationBottomSheet
import im.vector.app.features.debug.DebugMenuActivity
import im.vector.app.features.devtools.RoomDevToolActivity
import im.vector.app.features.home.room.detail.RoomDetailActivity
import im.vector.app.features.home.room.detail.RoomDetailArgs
import im.vector.app.features.home.room.detail.arguments.TimelineArgs
import im.vector.app.features.home.room.detail.search.SearchActivity
import im.vector.app.features.home.room.detail.search.SearchArgs
import im.vector.app.features.home.room.filtered.FilteredRoomsActivity
import im.vector.app.features.home.room.threads.ThreadsActivity
import im.vector.app.features.home.room.threads.arguments.ThreadListArgs
import im.vector.app.features.home.room.threads.arguments.ThreadTimelineArgs
import im.vector.app.features.invite.InviteUsersToRoomActivity
import im.vector.app.features.login.LoginActivity
import im.vector.app.features.login.LoginConfig
@ -118,7 +121,7 @@ class DefaultNavigator @Inject constructor(
fatalError("Trying to open an unknown room $roomId", vectorPreferences.failFast())
return
}
val args = RoomDetailArgs(roomId, eventId)
val args = TimelineArgs(roomId, eventId)
val intent = RoomDetailActivity.newIntent(context, args)
startActivity(context, intent, buildTask)
}
@ -141,7 +144,7 @@ class DefaultNavigator @Inject constructor(
startActivity(context, SpaceManageActivity.newIntent(context, spaceId, ManageType.AddRooms), false)
}
is Navigator.PostSwitchSpaceAction.OpenDefaultRoom -> {
val args = RoomDetailArgs(
val args = TimelineArgs(
postSwitchSpaceAction.roomId,
eventId = null,
openShareSpaceForId = spaceId.takeIf { postSwitchSpaceAction.showShareSheet }
@ -239,7 +242,7 @@ class DefaultNavigator @Inject constructor(
}
override fun openRoomForSharingAndFinish(activity: Activity, roomId: String, sharedData: SharedData) {
val args = RoomDetailArgs(roomId, null, sharedData)
val args = TimelineArgs(roomId, null, sharedData)
val intent = RoomDetailActivity.newIntent(activity, args)
activity.startActivity(intent)
activity.finish()
@ -507,4 +510,11 @@ class DefaultNavigator @Inject constructor(
context.startActivity(intent)
}
}
override fun openThread(context: Context, threadTimelineArgs: ThreadTimelineArgs) {
context.startActivity(ThreadsActivity.newIntent(
context = context,
threadTimelineArgs = threadTimelineArgs,
threadListArgs =null))
}
}

View file

@ -24,6 +24,7 @@ import androidx.activity.result.ActivityResultLauncher
import androidx.core.util.Pair
import im.vector.app.features.crypto.recover.SetupMode
import im.vector.app.features.displayname.getBestName
import im.vector.app.features.home.room.threads.arguments.ThreadTimelineArgs
import im.vector.app.features.login.LoginConfig
import im.vector.app.features.media.AttachmentData
import im.vector.app.features.pin.PinMode
@ -140,4 +141,7 @@ interface Navigator {
fun openDevTools(context: Context, roomId: String)
fun openCallTransfer(context: Context, callId: String)
fun openThread(context: Context, threadTimelineArgs: ThreadTimelineArgs)
}

View file

@ -56,7 +56,7 @@ import im.vector.app.features.call.webrtc.WebRtcCall
import im.vector.app.features.displayname.getBestName
import im.vector.app.features.home.HomeActivity
import im.vector.app.features.home.room.detail.RoomDetailActivity
import im.vector.app.features.home.room.detail.RoomDetailArgs
import im.vector.app.features.home.room.detail.arguments.TimelineArgs
import im.vector.app.features.settings.VectorPreferences
import im.vector.app.features.settings.troubleshoot.TestNotificationReceiver
import im.vector.app.features.themes.ThemeUtils
@ -497,7 +497,7 @@ class NotificationUtils @Inject constructor(private val context: Context,
val contentPendingIntent = TaskStackBuilder.create(context)
.addNextIntentWithParentStack(HomeActivity.newIntent(context))
.addNextIntent(RoomDetailActivity.newIntent(context, RoomDetailArgs(callInformation.nativeRoomId)))
.addNextIntent(RoomDetailActivity.newIntent(context, TimelineArgs(callInformation.nativeRoomId)))
.getPendingIntent(System.currentTimeMillis().toInt(), PendingIntent.FLAG_UPDATE_CURRENT)
builder.setContentIntent(contentPendingIntent)
@ -735,7 +735,7 @@ class NotificationUtils @Inject constructor(private val context: Context,
}
private fun buildOpenRoomIntent(roomId: String): PendingIntent? {
val roomIntentTap = RoomDetailActivity.newIntent(context, RoomDetailArgs(roomId))
val roomIntentTap = RoomDetailActivity.newIntent(context, TimelineArgs(roomId))
roomIntentTap.action = TAP_TO_VIEW_ACTION
// pending intent get reused by system, this will mess up the extra params, so put unique info to avoid that
roomIntentTap.data = Uri.parse("foobar://openRoom?$roomId")

View file

@ -0,0 +1,12 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M12.604,5.761L13.1741,5.1909C14.7488,3.6163 17.2867,3.6012 18.8428,5.1572C20.3988,6.7132 20.3837,9.251 18.809,10.8257L16.1004,13.5342C14.5257,15.1089 11.9877,15.124 10.4317,13.568M11.3962,18.2387L10.8259,18.8091C9.2512,20.3837 6.7133,20.3988 5.1572,18.8428C3.6012,17.2868 3.6163,14.749 5.191,13.1743L7.8995,10.4659C9.4741,8.8913 12.0121,8.8762 13.5681,10.4322"
android:strokeWidth="2"
android:fillColor="#00000000"
android:strokeColor="#737D8C"
android:strokeLineCap="round"/>
</vector>

View file

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M2,5C2,3.3431 3.3431,2 5,2H19C20.6565,2 22,3.3422 22,4.9994V17C22,18.6569 20.6569,20 19,20H7.6667C7.4503,20 7.2398,20.0702 7.0667,20.2L3.6,22.8C3.297,23.0273 2.8916,23.0638 2.5528,22.8944C2.214,22.725 2,22.3788 2,22V5ZM5,9C5,8.4477 5.4477,8 6,8H18C18.5523,8 19,8.4477 19,9C19,9.5523 18.5523,10 18,10H6C5.4477,10 5,9.5523 5,9ZM6,12C5.4477,12 5,12.4477 5,13C5,13.5523 5.4477,14 6,14H12C12.5523,14 13,13.5523 13,13C13,12.4477 12.5523,12 12,12H6Z"
android:fillColor="#000000"
android:fillType="evenOdd"/>
</vector>

View file

@ -0,0 +1,19 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M16,7C16,6.4477 16.4477,6 17,6C17.5523,6 18,6.4477 18,7C18,7.5523 17.5523,8 17,8C16.4477,8 16,7.5523 16,7ZM17,4C15.3431,4 14,5.3432 14,7C14,7.0876 14.0038,7.1744 14.0111,7.2601L8.1611,9.9192C7.6154,9.3525 6.8489,9 6,9C4.3432,9 3,10.3431 3,12C3,13.6569 4.3432,15 6,15C6.8488,15 7.6154,14.6474 8.1611,14.0808L14.0111,16.7399C14.0038,16.8256 14,16.9124 14,17C14,18.6569 15.3431,20 17,20C18.6569,20 20,18.6569 20,17C20,15.3431 18.6569,14 17,14C16.1511,14 15.3846,14.3526 14.8389,14.9192L8.9889,12.2601C8.9962,12.1744 9,12.0876 9,12C9,11.9124 8.9962,11.8256 8.9889,11.7399L14.8389,9.0808C15.3846,9.6475 16.1511,10 17,10C18.6569,10 20,8.6568 20,7C20,5.3432 18.6569,4 17,4ZM17,16C16.4477,16 16,16.4477 16,17C16,17.5523 16.4477,18 17,18C17.5523,18 18,17.5523 18,17C18,16.4477 17.5523,16 17,16ZM5,12C5,11.4477 5.4477,11 6,11C6.5523,11 7,11.4477 7,12C7,12.5523 6.5523,13 6,13C5.4477,13 5,12.5523 5,12Z"
android:fillColor="#737D8C"
android:fillType="evenOdd"/>
<path
android:pathData="M17,17m-1,0a1,1 0,1 1,2 0a1,1 0,1 1,-2 0"
android:fillColor="#737D8C"/>
<path
android:pathData="M17,7m-1,0a1,1 0,1 1,2 0a1,1 0,1 1,-2 0"
android:fillColor="#737D8C"/>
<path
android:pathData="M6,12m-1,0a1,1 0,1 1,2 0a1,1 0,1 1,-2 0"
android:fillColor="#737D8C"/>
</vector>

View file

@ -0,0 +1,30 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M3,4H21"
android:strokeWidth="2"
android:fillColor="#00000000"
android:strokeColor="#737D8C"
android:strokeLineCap="round"/>
<path
android:pathData="M3,10H16"
android:strokeWidth="2"
android:fillColor="#00000000"
android:strokeColor="#737D8C"
android:strokeLineCap="round"/>
<path
android:pathData="M3,14H11"
android:strokeWidth="2"
android:fillColor="#00000000"
android:strokeColor="#737D8C"
android:strokeLineCap="round"/>
<path
android:pathData="M3,20H21"
android:strokeWidth="2"
android:fillColor="#00000000"
android:strokeColor="#737D8C"
android:strokeLineCap="round"/>
</vector>

View file

@ -1,88 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/coordinatorLayout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/appBarLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent">
<com.google.android.material.appbar.MaterialToolbar
android:id="@+id/roomThreadDetailToolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:contentInsetStart="0dp">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/roomThreadDetailToolbarConstraintLayout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/roomThreadDetailToolbarTitleTextView"
style="@style/Widget.Vector.TextView.HeadlineMedium"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="12dp"
android:ellipsize="end"
android:maxLines="1"
android:text="@string/a11y_beta"
android:textColor="?vctr_content_primary"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:id="@+id/roomThreadDetailToolbarImageView"
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_marginStart="8dp"
android:importantForAccessibility="no"
android:src="@drawable/ic_presence_offline"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toEndOf="@id/roomThreadDetailToolbarTitleTextView"
app:layout_constraintTop_toTopOf="parent"
tools:src="@sample/room_round_avatars" />
<TextView
android:id="@+id/roomThreadDetailToolbarSubtitleTextView"
style="@style/Widget.Vector.TextView.Body"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="4dp"
android:ellipsize="end"
android:maxLines="1"
android:textColor="?vctr_content_primary"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toEndOf="@id/roomThreadDetailToolbarImageView"
app:layout_constraintTop_toTopOf="parent"
tools:text="RoomName"
tools:visibility="visible" />
</androidx.constraintlayout.widget.ConstraintLayout>
</com.google.android.material.appbar.MaterialToolbar>
</com.google.android.material.appbar.AppBarLayout>
<androidx.fragment.app.FragmentContainerView
android:id="@+id/roomThreadDetailFragmentContainer"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/appBarLayout" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>

View file

@ -1,88 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/coordinatorLayout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/appBarLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent">
<com.google.android.material.appbar.MaterialToolbar
android:id="@+id/roomThreadsToolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:contentInsetStart="0dp">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/roomThreadsToolbarConstraintLayout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/roomThreadsToolbarTitleTextView"
style="@style/Widget.Vector.TextView.HeadlineMedium"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="12dp"
android:ellipsize="end"
android:maxLines="1"
android:text="@string/a11y_beta"
android:textColor="?vctr_content_primary"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:id="@+id/roomThreadsToolbarImageView"
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_marginStart="8dp"
android:importantForAccessibility="no"
android:src="@drawable/ic_presence_offline"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toEndOf="@id/roomThreadsToolbarTitleTextView"
app:layout_constraintTop_toTopOf="parent"
tools:src="@sample/room_round_avatars" />
<TextView
android:id="@+id/roomThreadsToolbarSubtitleTextView"
style="@style/Widget.Vector.TextView.Body"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="4dp"
android:ellipsize="end"
android:maxLines="1"
android:textColor="?vctr_content_primary"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toEndOf="@id/roomThreadsToolbarImageView"
app:layout_constraintTop_toTopOf="parent"
tools:text="RoomName"
tools:visibility="visible" />
</androidx.constraintlayout.widget.ConstraintLayout>
</com.google.android.material.appbar.MaterialToolbar>
</com.google.android.material.appbar.AppBarLayout>
<androidx.fragment.app.FragmentContainerView
android:id="@+id/roomThreadsFragmentContainer"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/appBarLayout" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>

View file

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/coordinatorLayout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.fragment.app.FragmentContainerView
android:id="@+id/threadsActivityFragmentContainer"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>

View file

@ -26,101 +26,13 @@
android:layout_height="?actionBarSize"
android:transitionName="toolbar">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/roomToolbarContentView"
android:layout_width="match_parent"
android:layout_height="match_parent">
<include
android:id="@+id/includeThreadToolbar"
layout="@layout/view_room_detail_thread_toolbar" />
<ImageView
android:id="@+id/roomToolbarAvatarImageView"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp"
android:importantForAccessibility="no"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:src="@sample/room_round_avatars" />
<im.vector.app.core.ui.views.ShieldImageView
android:id="@+id/roomToolbarDecorationImageView"
android:layout_width="17dp"
android:layout_height="17dp"
android:layout_marginStart="5dp"
android:layout_marginTop="2dp"
app:layout_constraintBottom_toBottomOf="@+id/roomToolbarTitleView"
app:layout_constraintStart_toEndOf="@+id/roomToolbarAvatarImageView"
app:layout_constraintTop_toTopOf="@+id/roomToolbarTitleView" />
<im.vector.app.core.ui.views.PresenceStateImageView
android:id="@+id/roomToolbarPresenceImageView"
android:layout_width="12dp"
android:layout_height="12dp"
android:background="@drawable/background_circle"
android:padding="2dp"
android:visibility="gone"
app:layout_constraintCircle="@+id/roomToolbarAvatarImageView"
app:layout_constraintCircleAngle="135"
app:layout_constraintCircleRadius="20dp"
tools:ignore="MissingConstraints"
tools:layout_constraintCircleRadius="8dp"
tools:src="@drawable/ic_presence_offline"
tools:visibility="visible" />
<ImageView
android:id="@+id/roomToolbarPublicImageView"
android:layout_width="13dp"
android:layout_height="13dp"
android:background="@drawable/background_circle"
android:contentDescription="@string/a11y_public_room"
android:padding="1dp"
android:src="@drawable/ic_public_room"
android:visibility="gone"
app:layout_constraintCircle="@+id/roomToolbarAvatarImageView"
app:layout_constraintCircleAngle="135"
app:layout_constraintCircleRadius="20dp"
tools:ignore="MissingConstraints"
tools:visibility="visible" />
<TextView
android:id="@+id/roomToolbarTitleView"
style="@style/Widget.Vector.TextView.HeadlineMedium"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="4dp"
android:layout_marginEnd="8dp"
android:ellipsize="end"
android:maxLines="1"
android:textColor="?vctr_content_primary"
app:layout_constraintBottom_toTopOf="@+id/roomToolbarSubtitleView"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toEndOf="@+id/roomToolbarDecorationImageView"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_chainStyle="packed"
app:layout_goneMarginStart="7dp"
tools:text="@sample/rooms.json/data/name" />
<TextView
android:id="@+id/roomToolbarSubtitleView"
style="@style/Widget.Vector.TextView.Body"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="7dp"
android:layout_marginEnd="8dp"
android:ellipsize="end"
android:maxLines="1"
android:textColor="?vctr_content_primary"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toEndOf="@+id/roomToolbarAvatarImageView"
app:layout_constraintTop_toBottomOf="@+id/roomToolbarTitleView"
tools:text="@sample/rooms.json/data/topic"
tools:visibility="visible" />
</androidx.constraintlayout.widget.ConstraintLayout>
<include
android:id="@+id/includeRoomToolbar"
layout="@layout/view_room_detail_toolbar" />
</com.google.android.material.appbar.MaterialToolbar>

View file

@ -1,31 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/rootConstraintLayout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/roomThreadDetailRecyclerView"
android:layout_width="0dp"
android:layout_height="0dp"
android:overScrollMode="always"
app:layout_constraintBottom_toTopOf="@id/roomThreadDetailTextComposerView"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<im.vector.app.features.home.room.detail.composer.TextComposerView
android:id="@+id/roomThreadDetailTextComposerView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?android:colorBackground"
android:minHeight="56dp"
android:visibility="visible"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
tools:visibility="visible" />
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -0,0 +1,51 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/roomToolbarThreadConstraintLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:visibility="gone"
android:visibility="gone">
<TextView
android:id="@+id/roomToolbarThreadTitleTextView"
style="@style/Widget.Vector.TextView.HeadlineMedium"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ellipsize="end"
android:maxLines="1"
android:text="@string/room_threads_title"
android:textColor="?vctr_content_primary"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:id="@+id/roomToolbarThreadImageView"
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_marginStart="8dp"
android:importantForAccessibility="no"
android:src="@drawable/ic_presence_offline"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toEndOf="@id/roomToolbarThreadTitleTextView"
app:layout_constraintTop_toTopOf="parent"
tools:src="@sample/room_round_avatars" />
<TextView
android:id="@+id/roomToolbarThreadSubtitleTextView"
style="@style/Widget.Vector.TextView.Body"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="4dp"
android:ellipsize="end"
android:maxLines="1"
android:textColor="?vctr_content_primary"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toEndOf="@id/roomToolbarThreadImageView"
app:layout_constraintTop_toTopOf="parent"
tools:text="RoomName"
tools:visibility="visible" />
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -0,0 +1,99 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/roomToolbarContentView"
tools:visibility="visible"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/roomToolbarAvatarImageView"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp"
android:importantForAccessibility="no"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:src="@sample/room_round_avatars" />
<im.vector.app.core.ui.views.ShieldImageView
android:id="@+id/roomToolbarDecorationImageView"
android:layout_width="17dp"
android:layout_height="17dp"
android:layout_marginStart="5dp"
android:layout_marginTop="2dp"
app:layout_constraintBottom_toBottomOf="@+id/roomToolbarTitleView"
app:layout_constraintStart_toEndOf="@+id/roomToolbarAvatarImageView"
app:layout_constraintTop_toTopOf="@+id/roomToolbarTitleView" />
<im.vector.app.core.ui.views.PresenceStateImageView
android:id="@+id/roomToolbarPresenceImageView"
android:layout_width="12dp"
android:layout_height="12dp"
android:background="@drawable/background_circle"
android:padding="2dp"
android:visibility="gone"
app:layout_constraintCircle="@+id/roomToolbarAvatarImageView"
app:layout_constraintCircleAngle="135"
app:layout_constraintCircleRadius="20dp"
tools:ignore="MissingConstraints"
tools:layout_constraintCircleRadius="8dp"
tools:src="@drawable/ic_presence_offline"
tools:visibility="visible" />
<ImageView
android:id="@+id/roomToolbarPublicImageView"
android:layout_width="13dp"
android:layout_height="13dp"
android:background="@drawable/background_circle"
android:contentDescription="@string/a11y_public_room"
android:padding="1dp"
android:src="@drawable/ic_public_room"
android:visibility="gone"
app:layout_constraintCircle="@+id/roomToolbarAvatarImageView"
app:layout_constraintCircleAngle="135"
app:layout_constraintCircleRadius="20dp"
tools:ignore="MissingConstraints"
tools:visibility="visible" />
<TextView
android:id="@+id/roomToolbarTitleView"
style="@style/Widget.Vector.TextView.HeadlineMedium"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="4dp"
android:layout_marginEnd="8dp"
android:ellipsize="end"
android:maxLines="1"
android:textColor="?vctr_content_primary"
app:layout_constraintBottom_toTopOf="@+id/roomToolbarSubtitleView"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toEndOf="@+id/roomToolbarDecorationImageView"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_chainStyle="packed"
app:layout_goneMarginStart="7dp"
tools:text="@sample/rooms.json/data/name" />
<TextView
android:id="@+id/roomToolbarSubtitleView"
style="@style/Widget.Vector.TextView.Body"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="7dp"
android:layout_marginEnd="8dp"
android:ellipsize="end"
android:maxLines="1"
android:textColor="?vctr_content_primary"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toEndOf="@+id/roomToolbarAvatarImageView"
app:layout_constraintTop_toBottomOf="@+id/roomToolbarTitleView"
tools:text="@sample/rooms.json/data/topic"
tools:visibility="visible" />
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -0,0 +1,36 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/test"
android:icon="@drawable/ic_search"
android:title="@string/action_thread_view_in_room"
app:showAsAction="always">
<menu>
<item
android:id="@+id/menuWithIconText"
android:icon="@drawable/ic_thread_menu_item"
android:title="@string/action_thread_share"
/>
</menu>
</item>
<item
android:id="@+id/menu_thread_timeline_view_in_room"
android:icon="@drawable/ic_settings_x"
android:title="@string/action_thread_view_in_room"
app:showAsAction="never" />
<item
android:id="@+id/menu_thread_timeline_copy"
android:title="@string/action_thread_copy_link_to_thread"
app:showAsAction="never" />
<item
android:id="@+id/menu_thread_timeline_share"
android:title="@string/action_thread_share"
app:showAsAction="never" />
</menu>

View file

@ -37,11 +37,20 @@
app:showAsAction="always"
tools:visible="true" />
<item android:id="@+id/join_conference"
<item
android:id="@+id/threads"
android:icon="@drawable/ic_thread_menu_item"
android:title="@string/action_view_threads"
android:visible="false"
app:iconTint="?colorPrimary"
app:showAsAction="always"
tools:visible="true" />
<item
android:id="@+id/join_conference"
android:title="@string/join"
app:actionLayout="@layout/layout_join_conference_action"
app:showAsAction="always"
/>
app:showAsAction="always" />
<item
android:id="@+id/open_matrix_apps"
@ -57,4 +66,36 @@
app:showAsAction="never"
tools:visible="true" />
<item
android:id="@+id/menu_thread_timeline_more"
android:icon="@drawable/ic_more_vertical"
android:title="@string/action_thread_view_in_room"
app:iconTint="?vctr_content_secondary"
app:showAsAction="always">
<menu>
<item
android:id="@+id/menu_thread_timeline_view_in_room"
android:icon="@drawable/ic_thread_view_in_room_menu_item"
android:title="@string/action_thread_view_in_room"
app:iconTint="?vctr_content_secondary"
app:showAsAction="ifRoom" />
<item
android:id="@+id/menu_thread_timeline_copy_link"
android:icon="@drawable/ic_thread_link_menu_item"
android:title="@string/action_thread_copy_link_to_thread"
app:iconTint="?vctr_content_secondary"
app:showAsAction="ifRoom" />
<item
android:id="@+id/menu_thread_timeline_share"
android:icon="@drawable/ic_thread_share_menu_item"
android:title="@string/action_thread_share"
app:iconTint="?vctr_content_secondary"
app:showAsAction="ifRoom" />
</menu>
</item>
</menu>

View file

@ -441,6 +441,7 @@
<string name="action_sign_out_confirmation_simple">Are you sure you want to sign out?</string>
<string name="action_voice_call">Voice Call</string>
<string name="action_video_call">Video Call</string>
<string name="action_view_threads">View Threads</string>
<string name="action_global_search">Global search</string>
<string name="action_mark_all_as_read">Mark all as read</string>
<string name="action_historical">Historical</string>
@ -456,6 +457,11 @@
<string name="disable">Disable</string>
<string name="action_return">Return</string>
<!-- actions threads -->
<string name="action_thread_view_in_room">View in room</string>
<string name="action_thread_copy_link_to_thread">Copy link to thread</string>
<string name="action_thread_share">Share</string>
<!-- dialog titles -->
<string name="dialog_title_confirmation">Confirmation</string>
<string name="dialog_title_warning">Warning</string>
@ -1023,6 +1029,7 @@
<!-- Room Threads -->
<string name="room_threads_filter">Filter Threads in room</string>
<string name="room_threads_title">Thread</string>
<!-- Room events -->
<string name="room_event_action_report_prompt_reason">Reason for reporting this content</string>