Create direct room: add filtering and enhance design a bit

This commit is contained in:
ganfra 2019-07-18 17:42:22 +02:00
parent 4341b0d0f5
commit 001603cf9a
16 changed files with 357 additions and 52 deletions

View File

@ -20,6 +20,8 @@ import androidx.lifecycle.LiveData
import androidx.lifecycle.Observer import androidx.lifecycle.Observer
import io.reactivex.Observable import io.reactivex.Observable
import io.reactivex.android.MainThreadDisposable import io.reactivex.android.MainThreadDisposable
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.schedulers.Schedulers
private class LiveDataObservable<T>( private class LiveDataObservable<T>(
private val liveData: LiveData<T>, private val liveData: LiveData<T>,
@ -57,5 +59,5 @@ private class LiveDataObservable<T>(
} }
fun <T> LiveData<T>.asObservable(): Observable<T> { fun <T> LiveData<T>.asObservable(): Observable<T> {
return LiveDataObservable(this) return LiveDataObservable(this).observeOn(Schedulers.computation())
} }

View File

@ -26,19 +26,19 @@ import io.reactivex.schedulers.Schedulers
class RxRoom(private val room: Room) { class RxRoom(private val room: Room) {
fun liveRoomSummary(): Observable<RoomSummary> { fun liveRoomSummary(): Observable<RoomSummary> {
return room.liveRoomSummary().asObservable().observeOn(Schedulers.computation()) return room.liveRoomSummary().asObservable()
} }
fun liveRoomMemberIds(): Observable<List<String>> { fun liveRoomMemberIds(): Observable<List<String>> {
return room.getRoomMemberIdsLive().asObservable().observeOn(Schedulers.computation()) return room.getRoomMemberIdsLive().asObservable()
} }
fun liveAnnotationSummary(eventId: String): Observable<EventAnnotationsSummary> { fun liveAnnotationSummary(eventId: String): Observable<EventAnnotationsSummary> {
return room.getEventSummaryLive(eventId).asObservable().observeOn(Schedulers.computation()) return room.getEventSummaryLive(eventId).asObservable()
} }
fun liveTimelineEvent(eventId: String): Observable<TimelineEvent> { fun liveTimelineEvent(eventId: String): Observable<TimelineEvent> {
return room.liveTimeLineEvent(eventId).asObservable().observeOn(Schedulers.computation()) return room.liveTimeLineEvent(eventId).asObservable()
} }
} }

View File

@ -23,28 +23,29 @@ import im.vector.matrix.android.api.session.room.model.RoomSummary
import im.vector.matrix.android.api.session.sync.SyncState import im.vector.matrix.android.api.session.sync.SyncState
import im.vector.matrix.android.api.session.user.model.User import im.vector.matrix.android.api.session.user.model.User
import io.reactivex.Observable import io.reactivex.Observable
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.schedulers.Schedulers import io.reactivex.schedulers.Schedulers
class RxSession(private val session: Session) { class RxSession(private val session: Session) {
fun liveRoomSummaries(): Observable<List<RoomSummary>> { fun liveRoomSummaries(): Observable<List<RoomSummary>> {
return session.liveRoomSummaries().asObservable().observeOn(Schedulers.computation()) return session.liveRoomSummaries().asObservable()
} }
fun liveGroupSummaries(): Observable<List<GroupSummary>> { fun liveGroupSummaries(): Observable<List<GroupSummary>> {
return session.liveGroupSummaries().asObservable().observeOn(Schedulers.computation()) return session.liveGroupSummaries().asObservable()
} }
fun liveSyncState(): Observable<SyncState> { fun liveSyncState(): Observable<SyncState> {
return session.syncState().asObservable().observeOn(Schedulers.computation()) return session.syncState().asObservable()
} }
fun livePushers(): Observable<List<Pusher>> { fun livePushers(): Observable<List<Pusher>> {
return session.livePushers().asObservable().observeOn(Schedulers.computation()) return session.livePushers().asObservable()
} }
fun liveUsers(): Observable<List<User>> { fun liveUsers(): Observable<List<User>> {
return session.liveUsers().asObservable().observeOn(Schedulers.computation()) return session.liveUsers().asObservable()
} }
} }

View File

@ -23,6 +23,8 @@ import im.vector.matrix.android.api.session.user.UserService
import im.vector.matrix.android.api.session.user.model.User import im.vector.matrix.android.api.session.user.model.User
import im.vector.matrix.android.internal.database.RealmLiveData import im.vector.matrix.android.internal.database.RealmLiveData
import im.vector.matrix.android.internal.database.mapper.asDomain import im.vector.matrix.android.internal.database.mapper.asDomain
import im.vector.matrix.android.internal.database.model.RoomSummaryEntity
import im.vector.matrix.android.internal.database.model.RoomSummaryEntityFields
import im.vector.matrix.android.internal.database.model.UserEntity import im.vector.matrix.android.internal.database.model.UserEntity
import im.vector.matrix.android.internal.database.model.UserEntityFields import im.vector.matrix.android.internal.database.model.UserEntityFields
import im.vector.matrix.android.internal.database.query.where import im.vector.matrix.android.internal.database.query.where
@ -34,7 +36,7 @@ internal class DefaultUserService @Inject constructor(private val monarchy: Mona
override fun getUser(userId: String): User? { override fun getUser(userId: String): User? {
val userEntity = monarchy.fetchCopied { UserEntity.where(it, userId).findFirst() } val userEntity = monarchy.fetchCopied { UserEntity.where(it, userId).findFirst() }
?: return null ?: return null
return userEntity.asDomain() return userEntity.asDomain()
} }
@ -51,11 +53,13 @@ internal class DefaultUserService @Inject constructor(private val monarchy: Mona
} }
override fun liveUsers(): LiveData<List<User>> { override fun liveUsers(): LiveData<List<User>> {
val liveRealmData = RealmLiveData(monarchy.realmConfiguration) { realm -> return monarchy.findAllMappedWithChanges(
realm.where(UserEntity::class.java).sort(UserEntityFields.DISPLAY_NAME) { realm ->
} realm.where(UserEntity::class.java)
return Transformations.map(liveRealmData) { results -> .isNotEmpty(UserEntityFields.USER_ID)
results.map { it.asDomain() } .sort(UserEntityFields.DISPLAY_NAME)
} },
{ it.asDomain() }
)
} }
} }

View File

@ -0,0 +1,26 @@
/*
* Copyright 2019 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.riotx.features.home.createdirect
sealed class CreateDirectRoomActions {
object CreateRoomAndInviteSelectedUsers : CreateDirectRoomActions()
data class FilterKnownUsers(val value: String) : CreateDirectRoomActions()
object ClearFilterKnownUsers: CreateDirectRoomActions()
object SelectAddByMatrixId : CreateDirectRoomActions()
}

View File

@ -45,13 +45,17 @@ class CreateDirectRoomController @Inject constructor(private val avatarRenderer:
var lastFirstLetter: String? = null var lastFirstLetter: String? = null
knownUsers.forEach { user -> knownUsers.forEach { user ->
val currentFirstLetter = user.displayName.firstLetterOfDisplayName() val currentFirstLetter = user.displayName.firstLetterOfDisplayName()
val showLetter = lastFirstLetter != currentFirstLetter val showLetter = currentFirstLetter.isNotEmpty() && lastFirstLetter != currentFirstLetter
lastFirstLetter = currentFirstLetter lastFirstLetter = currentFirstLetter
CreateDirectRoomLetterHeaderItem_()
.id(currentFirstLetter)
.letter(currentFirstLetter)
.addIf(showLetter, this)
createDirectRoomUserItem { createDirectRoomUserItem {
id(user.userId) id(user.userId)
userId(user.userId) userId(user.userId)
showLetter(showLetter)
firstLetter(currentFirstLetter)
name(user.displayName) name(user.displayName)
avatarUrl(user.avatarUrl) avatarUrl(user.avatarUrl)
avatarRenderer(avatarRenderer) avatarRenderer(avatarRenderer)

View File

@ -19,18 +19,23 @@
package im.vector.riotx.features.home.createdirect package im.vector.riotx.features.home.createdirect
import android.os.Bundle import android.os.Bundle
import android.view.MenuItem
import com.airbnb.mvrx.fragmentViewModel import com.airbnb.mvrx.fragmentViewModel
import com.jakewharton.rxbinding3.appcompat.queryTextChanges
import im.vector.matrix.android.api.session.user.model.User import im.vector.matrix.android.api.session.user.model.User
import im.vector.riotx.R import im.vector.riotx.R
import im.vector.riotx.core.di.ScreenComponent import im.vector.riotx.core.di.ScreenComponent
import im.vector.riotx.core.platform.VectorBaseFragment import im.vector.riotx.core.platform.VectorBaseFragment
import kotlinx.android.synthetic.main.fragment_create_direct_room.* import kotlinx.android.synthetic.main.fragment_create_direct_room.*
import java.util.concurrent.TimeUnit
import javax.inject.Inject import javax.inject.Inject
class CreateDirectRoomFragment : VectorBaseFragment(), CreateDirectRoomController.Callback { class CreateDirectRoomFragment : VectorBaseFragment(), CreateDirectRoomController.Callback {
override fun getLayoutResId() = R.layout.fragment_create_direct_room override fun getLayoutResId() = R.layout.fragment_create_direct_room
override fun getMenuRes() = R.menu.vector_create_direct_room
private val viewModel: CreateDirectRoomViewModel by fragmentViewModel() private val viewModel: CreateDirectRoomViewModel by fragmentViewModel()
@Inject lateinit var createDirectRoomViewModelFactory: CreateDirectRoomViewModel.Factory @Inject lateinit var createDirectRoomViewModelFactory: CreateDirectRoomViewModel.Factory
@ -43,15 +48,43 @@ class CreateDirectRoomFragment : VectorBaseFragment(), CreateDirectRoomControlle
override fun onActivityCreated(savedInstanceState: Bundle?) { override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState) super.onActivityCreated(savedInstanceState)
setupRecyclerView() setupRecyclerView()
setupFilterView()
viewModel.subscribe(this) { renderState(it) } viewModel.subscribe(this) { renderState(it) }
} }
override fun onOptionsItemSelected(item: MenuItem): Boolean {
return when (item.itemId) {
R.id.action_create_room -> {
viewModel.handle(CreateDirectRoomActions.CreateRoomAndInviteSelectedUsers)
true
}
else ->
super.onOptionsItemSelected(item)
}
}
private fun setupRecyclerView() { private fun setupRecyclerView() {
recyclerView.setHasFixedSize(true) recyclerView.setHasFixedSize(true)
// Don't activate animation as we might have way to much item animation when filtering
recyclerView.itemAnimator = null
directRoomController.callback = this directRoomController.callback = this
recyclerView.setController(directRoomController) recyclerView.setController(directRoomController)
} }
private fun setupFilterView() {
createDirectRoomFilter
.queryTextChanges()
.subscribe {
val action = if (it.isNullOrEmpty()) {
CreateDirectRoomActions.ClearFilterKnownUsers
} else {
CreateDirectRoomActions.FilterKnownUsers(it.toString())
}
viewModel.handle(action)
}
.disposeOnDestroy()
}
private fun renderState(state: CreateDirectRoomViewState) { private fun renderState(state: CreateDirectRoomViewState) {
directRoomController.setData(state) directRoomController.setData(state)
} }

View File

@ -0,0 +1,39 @@
/*
* Copyright 2019 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.riotx.features.home.createdirect
import android.widget.TextView
import com.airbnb.epoxy.EpoxyAttribute
import com.airbnb.epoxy.EpoxyModelClass
import im.vector.riotx.R
import im.vector.riotx.core.epoxy.VectorEpoxyHolder
import im.vector.riotx.core.epoxy.VectorEpoxyModel
@EpoxyModelClass(layout = R.layout.item_create_direct_room_letter_header)
abstract class CreateDirectRoomLetterHeaderItem : VectorEpoxyModel<CreateDirectRoomLetterHeaderItem.Holder>() {
@EpoxyAttribute var letter: String = ""
override fun bind(holder: Holder) {
holder.letterView.text = letter
}
class Holder : VectorEpoxyHolder() {
val letterView by bind<TextView>(R.id.createDirectRoomLetterView)
}
}

View File

@ -32,8 +32,6 @@ import im.vector.riotx.features.home.AvatarRenderer
abstract class CreateDirectRoomUserItem : VectorEpoxyModel<CreateDirectRoomUserItem.Holder>() { abstract class CreateDirectRoomUserItem : VectorEpoxyModel<CreateDirectRoomUserItem.Holder>() {
@EpoxyAttribute lateinit var avatarRenderer: AvatarRenderer @EpoxyAttribute lateinit var avatarRenderer: AvatarRenderer
@EpoxyAttribute var showLetter: Boolean = false
@EpoxyAttribute var firstLetter: String = ""
@EpoxyAttribute var name: String? = null @EpoxyAttribute var name: String? = null
@EpoxyAttribute var userId: String = "" @EpoxyAttribute var userId: String = ""
@EpoxyAttribute var avatarUrl: String? = null @EpoxyAttribute var avatarUrl: String? = null
@ -41,14 +39,20 @@ abstract class CreateDirectRoomUserItem : VectorEpoxyModel<CreateDirectRoomUserI
override fun bind(holder: Holder) { override fun bind(holder: Holder) {
holder.view.setOnClickListener(clickListener) holder.view.setOnClickListener(clickListener)
holder.nameView.text = name // If name is empty, use userId as name and force it being centered
holder.letterView.visibility = if (showLetter) View.VISIBLE else View.INVISIBLE if (name.isNullOrEmpty()) {
holder.letterView.text = firstLetter holder.userIdView.visibility = View.GONE
holder.nameView.text = userId
} else {
holder.userIdView.visibility = View.VISIBLE
holder.nameView.text = name
holder.userIdView.text = userId
}
avatarRenderer.render(avatarUrl, userId, name, holder.avatarImageView) avatarRenderer.render(avatarUrl, userId, name, holder.avatarImageView)
} }
class Holder : VectorEpoxyHolder() { class Holder : VectorEpoxyHolder() {
val letterView by bind<TextView>(R.id.createDirectRoomUserLetter) val userIdView by bind<TextView>(R.id.createDirectRoomUserID)
val nameView by bind<TextView>(R.id.createDirectRoomUserName) val nameView by bind<TextView>(R.id.createDirectRoomUserName)
val avatarImageView by bind<ImageView>(R.id.createDirectRoomUserAvatar) val avatarImageView by bind<ImageView>(R.id.createDirectRoomUserAvatar)
} }

View File

@ -18,14 +18,23 @@
package im.vector.riotx.features.home.createdirect package im.vector.riotx.features.home.createdirect
import arrow.core.Option
import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.FragmentViewModelContext
import com.airbnb.mvrx.MvRxViewModelFactory import com.airbnb.mvrx.MvRxViewModelFactory
import com.airbnb.mvrx.ViewModelContext import com.airbnb.mvrx.ViewModelContext
import com.jakewharton.rxrelay2.BehaviorRelay
import com.squareup.inject.assisted.Assisted import com.squareup.inject.assisted.Assisted
import com.squareup.inject.assisted.AssistedInject import com.squareup.inject.assisted.AssistedInject
import im.vector.matrix.android.api.session.Session import im.vector.matrix.android.api.session.Session
import im.vector.matrix.android.api.session.user.model.User
import im.vector.matrix.rx.rx import im.vector.matrix.rx.rx
import im.vector.riotx.core.platform.VectorViewModel import im.vector.riotx.core.platform.VectorViewModel
import io.reactivex.Observable
import io.reactivex.functions.BiFunction
import io.reactivex.subjects.BehaviorSubject
import java.util.concurrent.TimeUnit
private typealias KnowUsersFilter = String
class CreateDirectRoomViewModel @AssistedInject constructor(@Assisted class CreateDirectRoomViewModel @AssistedInject constructor(@Assisted
initialState: CreateDirectRoomViewState, initialState: CreateDirectRoomViewState,
@ -37,6 +46,8 @@ class CreateDirectRoomViewModel @AssistedInject constructor(@Assisted
fun create(initialState: CreateDirectRoomViewState): CreateDirectRoomViewModel fun create(initialState: CreateDirectRoomViewState): CreateDirectRoomViewModel
} }
private val knownUsersFilter = BehaviorRelay.createDefault<Option<KnowUsersFilter>>(Option.empty())
companion object : MvRxViewModelFactory<CreateDirectRoomViewModel, CreateDirectRoomViewState> { companion object : MvRxViewModelFactory<CreateDirectRoomViewModel, CreateDirectRoomViewState> {
@JvmStatic @JvmStatic
@ -50,10 +61,42 @@ class CreateDirectRoomViewModel @AssistedInject constructor(@Assisted
observeKnownUsers() observeKnownUsers()
} }
private fun observeKnownUsers() { fun handle(createDirectRoomActions: CreateDirectRoomActions) {
session.rx().liveUsers().execute { when (createDirectRoomActions) {
this.copy(knownUsers = it) is CreateDirectRoomActions.CreateRoomAndInviteSelectedUsers -> createRoomAndInviteSelectedUsers()
is CreateDirectRoomActions.SelectAddByMatrixId -> handleSelectAddByMatrixId()
is CreateDirectRoomActions.FilterKnownUsers -> knownUsersFilter.accept(Option.just(createDirectRoomActions.value))
is CreateDirectRoomActions.ClearFilterKnownUsers -> knownUsersFilter.accept(Option.empty())
} }
} }
private fun handleSelectAddByMatrixId() {
// TODO
}
private fun createRoomAndInviteSelectedUsers() {
// TODO
}
private fun observeKnownUsers() {
Observable
.combineLatest<List<User>, Option<KnowUsersFilter>, List<User>>(
session.rx().liveUsers(),
knownUsersFilter.throttleLast(300, TimeUnit.MILLISECONDS),
BiFunction { users, filter ->
val filterValue = filter.orNull()
if (filterValue.isNullOrEmpty()) {
users
} else {
users.filter {
it.displayName?.contains(filterValue, ignoreCase = true) ?: false
|| it.userId.contains(filterValue, ignoreCase = true)
}
}
}
).execute { async ->
copy(knownUsers = async)
}
}
} }

View File

@ -24,5 +24,14 @@ import com.airbnb.mvrx.Uninitialized
import im.vector.matrix.android.api.session.user.model.User import im.vector.matrix.android.api.session.user.model.User
data class CreateDirectRoomViewState( data class CreateDirectRoomViewState(
val knownUsers: Async<List<User>> = Uninitialized val displayMode: DisplayMode = DisplayMode.KNOWN_USERS,
) : MvRxState val knownUsers: Async<List<User>> = Uninitialized,
val filteredKnownUsers: Async<List<User>> = Uninitialized
) : MvRxState {
enum class DisplayMode {
KNOWN_USERS,
MATRIX_ID_USERS
}
}

View File

@ -1,14 +1,113 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<im.vector.riotx.core.platform.StateView xmlns:android="http://schemas.android.com/apk/res/android" <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" xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/stateView"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent">
<com.airbnb.epoxy.EpoxyRecyclerView <androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/recyclerView"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent">
tools:listitem="@layout/item_create_direct_room_user" />
<androidx.appcompat.widget.Toolbar
android:id="@+id/createRoomToolbar"
style="@style/VectorToolbarStyle"
android:layout_width="0dp"
android:layout_height="?actionBarSize"
android:elevation="4dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/createDirectRoomClose"
android:layout_width="@dimen/layout_touch_size"
android:layout_height="@dimen/layout_touch_size"
android:scaleType="center"
android:src="@drawable/ic_x_18dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/createDirectRoomTitle"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
android:ellipsize="end"
android:maxLines="1"
android:text="@string/direct_chats_header"
android:textColor="?riotx_text_primary"
android:textSize="18sp"
android:textStyle="bold"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toEndOf="@+id/createDirectRoomClose"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.appcompat.widget.Toolbar>
<androidx.cardview.widget.CardView
android:id="@+id/createDirectRoomFilterContainer"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:cardElevation="4dp"
app:cardUseCompatPadding="true"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/createRoomToolbar">
<androidx.appcompat.widget.SearchView
android:id="@+id/createDirectRoomFilter"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:closeIcon="@drawable/ic_x_green"
app:iconifiedByDefault="false"
app:queryBackground="@android:color/transparent"
app:queryHint="@string/room_directory_search_hint"
app:searchIcon="@drawable/ic_filter" />
</androidx.cardview.widget.CardView>
<com.google.android.material.button.MaterialButton
android:id="@+id/addByMatrixId"
style="@style/VectorButtonStyleFlat"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginLeft="16dp"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp"
android:minHeight="@dimen/layout_touch_size"
android:text="@string/add_by_matrix_id"
app:icon="@drawable/ic_plus_circle"
app:iconPadding="13dp"
app:iconTint="@color/riotx_accent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/createDirectRoomFilterContainer" />
<com.airbnb.epoxy.EpoxyRecyclerView
android:id="@+id/recyclerView"
android:layout_width="0dp"
android:layout_height="0dp"
android:fastScrollEnabled="true"
android:scrollbars="vertical"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/addByMatrixId"
tools:listitem="@layout/item_create_direct_room_user" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
</im.vector.riotx.core.platform.StateView>

View File

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/createDirectRoomLetterView"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="8dp"
android:fontFamily="sans-serif-medium"
android:padding="8dp"
android:textColor="?attr/riotx_text_primary"
android:textSize="20sp"
android:textStyle="normal"
tools:text="C" />

View File

@ -1,7 +1,8 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <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" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@ -10,26 +11,18 @@
android:orientation="horizontal" android:orientation="horizontal"
android:padding="8dp"> android:padding="8dp">
<TextView
android:id="@+id/createDirectRoomUserLetter"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="sans-serif-medium"
android:textColor="#7e899c"
android:textSize="20sp"
android:textStyle="normal"
tools:text="C" />
<ImageView <ImageView
android:id="@+id/createDirectRoomUserAvatar" android:id="@+id/createDirectRoomUserAvatar"
android:layout_width="40dp" android:layout_width="40dp"
android:layout_height="40dp" android:layout_height="40dp"
android:layout_marginStart="48dp" android:layout_marginStart="8dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:src="@tools:sample/avatars" /> tools:src="@tools:sample/avatars" />
<TextView <TextView
android:id="@+id/createDirectRoomUserName" android:id="@+id/createDirectRoomUserName"
android:layout_width="wrap_content" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="center_vertical" android:layout_gravity="center_vertical"
android:layout_marginStart="12dp" android:layout_marginStart="12dp"
@ -38,6 +31,26 @@
android:textColor="?riotx_text_primary" android:textColor="?riotx_text_primary"
android:textSize="15sp" android:textSize="15sp"
android:textStyle="bold" android:textStyle="bold"
app:layout_constraintBottom_toTopOf="@+id/createDirectRoomUserID"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toEndOf="@+id/createDirectRoomUserAvatar"
app:layout_constraintTop_toTopOf="parent"
tools:text="@tools:sample/full_names" /> tools:text="@tools:sample/full_names" />
</LinearLayout> <TextView
android:id="@+id/createDirectRoomUserID"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:ellipsize="end"
android:maxLines="1"
android:textColor="?riotx_text_secondary"
android:textSize="15sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="@+id/createDirectRoomUserName"
app:layout_constraintTop_toBottomOf="@+id/createDirectRoomUserName"
tools:text="Blabla" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -0,0 +1,12 @@
<?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"
xmlns:tools="http://schemas.android.com/tools"
tools:context=".features.roomdirectory.RoomDirectoryActivity">
<item
android:id="@+id/action_create_room"
android:title="@string/create_room_action_create"
app:showAsAction="always" />
</menu>

View File

@ -39,4 +39,6 @@
<string name="link_copied_to_clipboard">Link copied to clipboard</string> <string name="link_copied_to_clipboard">Link copied to clipboard</string>
<string name="add_by_matrix_id">Add by matrix ID</string>
</resources> </resources>