Merge branch 'develop' into feature/fix_reply_tag

This commit is contained in:
Valere 2020-07-28 16:58:00 +02:00 committed by GitHub
commit a47ff99be7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
49 changed files with 738 additions and 251 deletions

View File

@ -7,6 +7,8 @@ Features ✨:
Improvements 🙌:
- Sending events is now retried only 3 times, so we avoid blocking the sending queue too long.
- Display warning when fail to send events in room list
- Improve UI of edit role action in member profile
- Moderation | New screen to display list of banned users in room settings, with unban action
Bugfix 🐛:
- Fix theme issue on Room directory screen (#1613)
@ -17,6 +19,9 @@ Bugfix 🐛:
- Fix Infinite loop at startup when migrating account from Riot (#1699)
- Fix Element crashes in loop after initial sync (#1709)
- Remove inner mx-reply tags before replying
- Fix timeline items not loading when there are only filtered events
- Fix "Voice & Video" grayed out in Settings (#1733)
- Fix Allow VOIP call in all rooms with 2 participants (even if not DM)
Translations 🗣:
-

View File

@ -67,7 +67,7 @@ data class RoomSummary constructor(
get() = tags.any { it.name == RoomTag.ROOM_TAG_FAVOURITE }
val canStartCall: Boolean
get() = isDirect && joinedMembersCount == 2
get() = joinedMembersCount == 2
companion object {
const val NOT_IN_BREADCRUMBS = -1

View File

@ -169,7 +169,7 @@ internal class DefaultTimeline(
filteredEvents = nonFilteredEvents.where()
.filterEventsWithSettings()
.findAll()
filteredEvents.addChangeListener(eventsChangeListener)
nonFilteredEvents.addChangeListener(eventsChangeListener)
handleInitialLoad()
if (settings.shouldHandleHiddenReadReceipts()) {
hiddenReadReceipts.start(realm, filteredEvents, nonFilteredEvents, this)

View File

@ -77,6 +77,7 @@ import im.vector.riotx.features.roommemberprofile.RoomMemberProfileFragment
import im.vector.riotx.features.roommemberprofile.devices.DeviceListFragment
import im.vector.riotx.features.roommemberprofile.devices.DeviceTrustInfoActionFragment
import im.vector.riotx.features.roomprofile.RoomProfileFragment
import im.vector.riotx.features.roomprofile.banned.RoomBannedMemberListFragment
import im.vector.riotx.features.roomprofile.members.RoomMemberListFragment
import im.vector.riotx.features.roomprofile.settings.RoomSettingsFragment
import im.vector.riotx.features.roomprofile.uploads.RoomUploadsFragment
@ -534,4 +535,9 @@ interface FragmentModule {
@IntoMap
@FragmentKey(ContactsBookFragment::class)
fun bindPhoneBookFragment(fragment: ContactsBookFragment): Fragment
@Binds
@IntoMap
@FragmentKey(RoomBannedMemberListFragment::class)
fun bindRoomBannedMemberListFragment(fragment: RoomBannedMemberListFragment): Fragment
}

View File

@ -16,7 +16,9 @@
package im.vector.riotx.core.epoxy
import android.widget.ProgressBar
import android.widget.TextView
import androidx.core.view.isVisible
import com.airbnb.epoxy.EpoxyAttribute
import com.airbnb.epoxy.EpoxyModelClass
import im.vector.riotx.R
@ -26,14 +28,16 @@ import im.vector.riotx.core.extensions.setTextOrHide
abstract class LoadingItem : VectorEpoxyModel<LoadingItem.Holder>() {
@EpoxyAttribute var loadingText: String? = null
@EpoxyAttribute var showLoader: Boolean = true
override fun bind(holder: Holder) {
super.bind(holder)
holder.progressBar.isVisible = showLoader
holder.textView.setTextOrHide(loadingText)
}
class Holder : VectorEpoxyHolder() {
val textView by bind<TextView>(R.id.loadingText)
val progressBar by bind<ProgressBar>(R.id.loadingProgress)
}
}

View File

@ -0,0 +1,51 @@
/*
* Copyright (c) 2020 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.core.epoxy.profiles
import android.view.View
import androidx.core.view.isVisible
import com.airbnb.epoxy.EpoxyAttribute
import im.vector.matrix.android.api.crypto.RoomEncryptionTrustLevel
import im.vector.matrix.android.api.util.MatrixItem
import im.vector.riotx.core.epoxy.VectorEpoxyModel
import im.vector.riotx.core.extensions.setTextOrHide
import im.vector.riotx.features.crypto.util.toImageRes
import im.vector.riotx.features.home.AvatarRenderer
abstract class BaseProfileMatrixItem<T : ProfileMatrixItem.Holder> : VectorEpoxyModel<T>() {
@EpoxyAttribute lateinit var avatarRenderer: AvatarRenderer
@EpoxyAttribute lateinit var matrixItem: MatrixItem
@EpoxyAttribute var editable: Boolean = true
@EpoxyAttribute
var userEncryptionTrustLevel: RoomEncryptionTrustLevel? = null
@EpoxyAttribute var clickListener: View.OnClickListener? = null
override fun bind(holder: T) {
super.bind(holder)
val bestName = matrixItem.getBestName()
val matrixId = matrixItem.id
.takeIf { it != bestName }
// Special case for ThreePid fake matrix item
.takeIf { it != "@" }
holder.view.setOnClickListener(clickListener?.takeIf { editable })
holder.titleView.text = bestName
holder.subtitleView.setTextOrHide(matrixId)
holder.editableView.isVisible = editable
avatarRenderer.render(matrixItem, holder.avatarImageView)
holder.avatarDecorationImageView.setImageResource(userEncryptionTrustLevel.toImageRes())
}
}

View File

@ -20,43 +20,14 @@ package im.vector.riotx.core.epoxy.profiles
import android.view.View
import android.widget.ImageView
import android.widget.TextView
import androidx.core.view.isVisible
import com.airbnb.epoxy.EpoxyAttribute
import com.airbnb.epoxy.EpoxyModelClass
import im.vector.matrix.android.api.crypto.RoomEncryptionTrustLevel
import im.vector.matrix.android.api.util.MatrixItem
import im.vector.riotx.R
import im.vector.riotx.core.epoxy.VectorEpoxyHolder
import im.vector.riotx.core.epoxy.VectorEpoxyModel
import im.vector.riotx.core.extensions.setTextOrHide
import im.vector.riotx.features.crypto.util.toImageRes
import im.vector.riotx.features.home.AvatarRenderer
@EpoxyModelClass(layout = R.layout.item_profile_matrix_item)
abstract class ProfileMatrixItem : VectorEpoxyModel<ProfileMatrixItem.Holder>() {
abstract class ProfileMatrixItem : BaseProfileMatrixItem<ProfileMatrixItem.Holder>() {
@EpoxyAttribute lateinit var avatarRenderer: AvatarRenderer
@EpoxyAttribute lateinit var matrixItem: MatrixItem
@EpoxyAttribute var editable: Boolean = true
@EpoxyAttribute var userEncryptionTrustLevel: RoomEncryptionTrustLevel? = null
@EpoxyAttribute var clickListener: View.OnClickListener? = null
override fun bind(holder: Holder) {
super.bind(holder)
val bestName = matrixItem.getBestName()
val matrixId = matrixItem.id
.takeIf { it != bestName }
// Special case for ThreePid fake matrix item
.takeIf { it != "@" }
holder.view.setOnClickListener(clickListener?.takeIf { editable })
holder.titleView.text = bestName
holder.subtitleView.setTextOrHide(matrixId)
holder.editableView.isVisible = editable
avatarRenderer.render(matrixItem, holder.avatarImageView)
holder.avatarDecorationImageView.setImageResource(userEncryptionTrustLevel.toImageRes())
}
class Holder : VectorEpoxyHolder() {
open class Holder : VectorEpoxyHolder() {
val titleView by bind<TextView>(R.id.matrixItemTitle)
val subtitleView by bind<TextView>(R.id.matrixItemSubtitle)
val avatarImageView by bind<ImageView>(R.id.matrixItemAvatar)

View File

@ -0,0 +1,39 @@
/*
* Copyright 2020 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.core.epoxy.profiles
import android.widget.ProgressBar
import androidx.core.view.isVisible
import com.airbnb.epoxy.EpoxyAttribute
import com.airbnb.epoxy.EpoxyModelClass
import im.vector.riotx.R
@EpoxyModelClass(layout = R.layout.item_profile_matrix_item_progress)
abstract class ProfileMatrixItemWithProgress : BaseProfileMatrixItem<ProfileMatrixItemWithProgress.Holder>() {
@EpoxyAttribute var inProgress: Boolean = true
override fun bind(holder: Holder) {
super.bind(holder)
holder.progress.isVisible = inProgress
}
class Holder : ProfileMatrixItem.Holder() {
val progress by bind<ProgressBar>(R.id.matrixItemProgress)
}
}

View File

@ -74,7 +74,8 @@ class TimelineEventController @Inject constructor(private val dateFormatter: Vec
fun onEncryptedMessageClicked(informationData: MessageInformationData, view: View)
fun onImageMessageClicked(messageImageContent: MessageImageInfoContent, mediaData: ImageContentRenderer.Data, view: View)
fun onVideoMessageClicked(messageVideoContent: MessageVideoContent, mediaData: VideoContentRenderer.Data, view: View)
// fun onFileMessageClicked(eventId: String, messageFileContent: MessageFileContent)
// fun onFileMessageClicked(eventId: String, messageFileContent: MessageFileContent)
// fun onAudioMessageClicked(messageAudioContent: MessageAudioContent)
fun onEditedDecorationClicked(informationData: MessageInformationData)
@ -107,7 +108,6 @@ class TimelineEventController @Inject constructor(private val dateFormatter: Vec
fun onUrlLongClicked(url: String): Boolean
}
private var showingForwardLoader = false
// Map eventId to adapter position
private val adapterPositionMapping = HashMap<String, Int>()
private val modelCache = arrayListOf<CacheItemData?>()
@ -233,7 +233,8 @@ class TimelineEventController @Inject constructor(private val dateFormatter: Vec
override fun buildModels() {
val timestamp = System.currentTimeMillis()
showingForwardLoader = LoadingItem_()
val showingForwardLoader = LoadingItem_()
.id("forward_loading_item_$timestamp")
.setVisibilityStateChangedListener(Timeline.Direction.FORWARDS)
.addWhenLoading(Timeline.Direction.FORWARDS)
@ -242,12 +243,13 @@ class TimelineEventController @Inject constructor(private val dateFormatter: Vec
add(timelineModels)
// Avoid displaying two loaders if there is no elements between them
if (!showingForwardLoader || timelineModels.isNotEmpty()) {
LoadingItem_()
.id("backward_loading_item_$timestamp")
.setVisibilityStateChangedListener(Timeline.Direction.BACKWARDS)
.addWhenLoading(Timeline.Direction.BACKWARDS)
}
val showBackwardsLoader = !showingForwardLoader || timelineModels.isNotEmpty()
// We can hide the loader but still add the item to controller so it can trigger backwards pagination
LoadingItem_()
.id("backward_loading_item_$timestamp")
.setVisibilityStateChangedListener(Timeline.Direction.BACKWARDS)
.showLoader(showBackwardsLoader)
.addWhenLoading(Timeline.Direction.BACKWARDS)
}
// Timeline.LISTENER ***************************************************************************

View File

@ -19,12 +19,10 @@ package im.vector.riotx.features.login
import android.content.Context
import android.content.Intent
import android.view.View
import android.view.ViewGroup
import androidx.annotation.CallSuper
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.widget.Toolbar
import androidx.core.view.ViewCompat
import androidx.core.view.children
import androidx.core.view.isVisible
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentManager
@ -73,14 +71,6 @@ open class LoginActivity : VectorBaseActivity(), ToolbarConfigurable {
get() = supportFragmentManager.findFragmentById(R.id.loginFragmentContainer)
private val commonOption: (FragmentTransaction) -> Unit = { ft ->
// Find the loginLogo on the current Fragment, this should not return null
(topFragment?.view as? ViewGroup)
// Find findViewById does not work, I do not know why
// findViewById<View?>(R.id.loginLogo)
?.children
?.firstOrNull { it.id == R.id.loginLogo }
?.let { ft.addSharedElement(it, ViewCompat.getTransitionName(it) ?: "") }
// TODO
ft.setCustomAnimations(enterAnim, exitAnim, popEnterAnim, popExitAnim)
}
@ -145,7 +135,6 @@ open class LoginActivity : VectorBaseActivity(), ToolbarConfigurable {
addFragmentToBackstack(R.id.loginFragmentContainer,
LoginServerSelectionFragment::class.java,
option = { ft ->
findViewById<View?>(R.id.loginSplashLogo)?.let { ft.addSharedElement(it, ViewCompat.getTransitionName(it) ?: "") }
findViewById<View?>(R.id.loginSplashTitle)?.let { ft.addSharedElement(it, ViewCompat.getTransitionName(it) ?: "") }
findViewById<View?>(R.id.loginSplashSubmit)?.let { ft.addSharedElement(it, ViewCompat.getTransitionName(it) ?: "") }
// TODO Disabled because it provokes a flickering

View File

@ -236,10 +236,12 @@ class RoomMemberProfileController @Inject constructor(
if (canEditPowerLevel) {
buildProfileAction(
id = "edit_power_level",
editable = false,
title = powerLevelsStr,
editable = true,
title = stringProvider.getString(R.string.power_level_title),
subtitle = powerLevelsStr,
divider = canKick || canBan,
dividerColor = dividerColor,
editableRes = R.drawable.ic_edit,
action = { callback?.onEditPowerLevel(userPowerLevel) }
)
}

View File

@ -32,6 +32,7 @@ import im.vector.riotx.core.platform.VectorBaseActivity
import im.vector.riotx.features.room.RequireActiveMembershipViewEvents
import im.vector.riotx.features.room.RequireActiveMembershipViewModel
import im.vector.riotx.features.room.RequireActiveMembershipViewState
import im.vector.riotx.features.roomprofile.banned.RoomBannedMemberListFragment
import im.vector.riotx.features.roomprofile.members.RoomMemberListFragment
import im.vector.riotx.features.roomprofile.settings.RoomSettingsFragment
import im.vector.riotx.features.roomprofile.uploads.RoomUploadsFragment
@ -81,9 +82,10 @@ class RoomProfileActivity :
.observe()
.subscribe { sharedAction ->
when (sharedAction) {
is RoomProfileSharedAction.OpenRoomMembers -> openRoomMembers()
is RoomProfileSharedAction.OpenRoomSettings -> openRoomSettings()
is RoomProfileSharedAction.OpenRoomUploads -> openRoomUploads()
is RoomProfileSharedAction.OpenRoomMembers -> openRoomMembers()
is RoomProfileSharedAction.OpenRoomSettings -> openRoomSettings()
is RoomProfileSharedAction.OpenRoomUploads -> openRoomUploads()
is RoomProfileSharedAction.OpenBannedRoomMembers -> openBannedRoomMembers()
}
}
.disposeOnDestroy()
@ -114,6 +116,10 @@ class RoomProfileActivity :
addFragmentToBackstack(R.id.simpleFragmentContainer, RoomMemberListFragment::class.java, roomProfileArgs)
}
private fun openBannedRoomMembers() {
addFragmentToBackstack(R.id.simpleFragmentContainer, RoomBannedMemberListFragment::class.java, roomProfileArgs)
}
override fun configure(toolbar: Toolbar) {
configureToolbar(toolbar)
}

View File

@ -41,6 +41,7 @@ class RoomProfileController @Inject constructor(
interface Callback {
fun onLearnMoreClicked()
fun onMemberListClicked()
fun onBannedMemberListClicked()
fun onNotificationsClicked()
fun onUploadsClicked()
fun onSettingsClicked()
@ -92,6 +93,16 @@ class RoomProfileController @Inject constructor(
accessory = R.drawable.ic_shield_warning.takeIf { hasWarning } ?: 0,
action = { callback?.onMemberListClicked() }
)
if (data.bannedMembership.invoke()?.isNotEmpty() == true) {
buildProfileAction(
id = "banned_list",
title = stringProvider.getString(R.string.room_settings_banned_users_title),
dividerColor = dividerColor,
icon = R.drawable.ic_settings_root_labs,
action = { callback?.onBannedMemberListClicked() }
)
}
buildProfileAction(
id = "uploads",
title = stringProvider.getString(R.string.room_profile_section_more_uploads),

View File

@ -206,6 +206,10 @@ class RoomProfileFragment @Inject constructor(
roomProfileSharedActionViewModel.post(RoomProfileSharedAction.OpenRoomMembers)
}
override fun onBannedMemberListClicked() {
roomProfileSharedActionViewModel.post(RoomProfileSharedAction.OpenBannedRoomMembers)
}
override fun onSettingsClicked() {
roomProfileSharedActionViewModel.post(RoomProfileSharedAction.OpenRoomSettings)
}

View File

@ -25,4 +25,5 @@ sealed class RoomProfileSharedAction : VectorSharedAction {
object OpenRoomSettings : RoomProfileSharedAction()
object OpenRoomUploads : RoomProfileSharedAction()
object OpenRoomMembers : RoomProfileSharedAction()
object OpenBannedRoomMembers : RoomProfileSharedAction()
}

View File

@ -26,6 +26,8 @@ import im.vector.matrix.android.api.MatrixCallback
import im.vector.matrix.android.api.permalinks.PermalinkFactory
import im.vector.matrix.android.api.session.Session
import im.vector.matrix.android.api.session.events.model.EventType
import im.vector.matrix.android.api.session.room.members.roomMemberQueryParams
import im.vector.matrix.android.api.session.room.model.Membership
import im.vector.matrix.android.api.session.room.powerlevels.PowerLevelsHelper
import im.vector.matrix.rx.rx
import im.vector.matrix.rx.unwrap
@ -61,7 +63,8 @@ class RoomProfileViewModel @AssistedInject constructor(@Assisted private val ini
}
private fun observeRoomSummary() {
room.rx().liveRoomSummary()
val rxRoom = room.rx()
rxRoom.liveRoomSummary()
.unwrap()
.execute {
copy(roomSummary = it)
@ -73,10 +76,17 @@ class RoomProfileViewModel @AssistedInject constructor(@Assisted private val ini
.subscribe {
val powerLevelsHelper = PowerLevelsHelper(it)
setState {
copy(canChangeAvatar = powerLevelsHelper.isUserAllowedToSend(session.myUserId, true, EventType.STATE_ROOM_AVATAR))
copy(canChangeAvatar = powerLevelsHelper.isUserAllowedToSend(session.myUserId, true, EventType.STATE_ROOM_AVATAR))
}
}
.disposeOnClear()
rxRoom.liveRoomMembers(roomMemberQueryParams { memberships = listOf(Membership.BAN) })
.execute {
copy(
bannedMembership = it
)
}
}
override fun handle(action: RoomProfileAction) = when (action) {

View File

@ -20,11 +20,13 @@ package im.vector.riotx.features.roomprofile
import com.airbnb.mvrx.Async
import com.airbnb.mvrx.MvRxState
import com.airbnb.mvrx.Uninitialized
import im.vector.matrix.android.api.session.room.model.RoomMemberSummary
import im.vector.matrix.android.api.session.room.model.RoomSummary
data class RoomProfileViewState(
val roomId: String,
val roomSummary: Async<RoomSummary> = Uninitialized,
val bannedMembership: Async<List<RoomMemberSummary>> = Uninitialized,
val canChangeAvatar: Boolean = false
) : MvRxState {

View File

@ -0,0 +1,25 @@
/*
* Copyright (c) 2020 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.roomprofile.banned
import im.vector.matrix.android.api.session.room.model.RoomMemberSummary
import im.vector.riotx.core.platform.VectorViewModelAction
sealed class RoomBannedListMemberAction : VectorViewModelAction {
data class QueryInfo(val roomMemberSummary: RoomMemberSummary) : RoomBannedListMemberAction()
data class UnBanUser(val roomMemberSummary: RoomMemberSummary) : RoomBannedListMemberAction()
}

View File

@ -0,0 +1,130 @@
/*
* Copyright (c) 2020 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.roomprofile.banned
import androidx.lifecycle.viewModelScope
import com.airbnb.mvrx.FragmentViewModelContext
import com.airbnb.mvrx.MvRxViewModelFactory
import com.airbnb.mvrx.ViewModelContext
import com.squareup.inject.assisted.Assisted
import com.squareup.inject.assisted.AssistedInject
import im.vector.matrix.android.api.query.QueryStringValue
import im.vector.matrix.android.api.session.Session
import im.vector.matrix.android.api.session.events.model.EventType
import im.vector.matrix.android.api.session.events.model.toModel
import im.vector.matrix.android.api.session.room.members.roomMemberQueryParams
import im.vector.matrix.android.api.session.room.model.Membership
import im.vector.matrix.android.api.session.room.model.RoomMemberContent
import im.vector.matrix.android.api.session.room.model.RoomMemberSummary
import im.vector.matrix.android.api.session.room.powerlevels.PowerLevelsHelper
import im.vector.matrix.android.internal.util.awaitCallback
import im.vector.matrix.rx.rx
import im.vector.matrix.rx.unwrap
import im.vector.riotx.R
import im.vector.riotx.core.platform.VectorViewModel
import im.vector.riotx.core.resources.StringProvider
import im.vector.riotx.features.powerlevel.PowerLevelsObservableFactory
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
class RoomBannedListMemberViewModel @AssistedInject constructor(@Assisted initialState: RoomBannedMemberListViewState,
private val stringProvider: StringProvider,
private val session: Session)
: VectorViewModel<RoomBannedMemberListViewState, RoomBannedListMemberAction, RoomBannedViewEvents>(initialState) {
@AssistedInject.Factory
interface Factory {
fun create(initialState: RoomBannedMemberListViewState): RoomBannedListMemberViewModel
}
private val room = session.getRoom(initialState.roomId)!!
init {
val rxRoom = room.rx()
room.rx().liveRoomSummary()
.unwrap()
.execute { async ->
copy(roomSummary = async)
}
rxRoom.liveRoomMembers(roomMemberQueryParams { memberships = listOf(Membership.BAN) })
.execute {
copy(
bannedMemberSummaries = it
)
}
val powerLevelsContentLive = PowerLevelsObservableFactory(room).createObservable()
powerLevelsContentLive.subscribe {
val powerLevelsHelper = PowerLevelsHelper(it)
setState { copy(canUserBan = powerLevelsHelper.isUserAbleToBan(session.myUserId)) }
}.disposeOnClear()
}
companion object : MvRxViewModelFactory<RoomBannedListMemberViewModel, RoomBannedMemberListViewState> {
@JvmStatic
override fun create(viewModelContext: ViewModelContext, state: RoomBannedMemberListViewState): RoomBannedListMemberViewModel? {
val fragment: RoomBannedMemberListFragment = (viewModelContext as FragmentViewModelContext).fragment()
return fragment.viewModelFactory.create(state)
}
}
override fun handle(action: RoomBannedListMemberAction) {
when (action) {
is RoomBannedListMemberAction.QueryInfo -> onQueryBanInfo(action.roomMemberSummary)
is RoomBannedListMemberAction.UnBanUser -> unBanUser(action.roomMemberSummary)
}
}
private fun onQueryBanInfo(roomMemberSummary: RoomMemberSummary) {
val bannedEvent = room.getStateEvent(EventType.STATE_ROOM_MEMBER, QueryStringValue.Equals(roomMemberSummary.userId))
val content = bannedEvent?.getClearContent().toModel<RoomMemberContent>()
if (content?.membership != Membership.BAN) {
// may be report error?
return
}
val reason = content.reason
val bannedBy = bannedEvent?.senderId ?: return
_viewEvents.post(RoomBannedViewEvents.ShowBannedInfo(bannedBy, reason ?: "", roomMemberSummary))
}
private fun unBanUser(roomMemberSummary: RoomMemberSummary) {
setState {
copy(onGoingModerationAction = this.onGoingModerationAction + roomMemberSummary.userId)
}
viewModelScope.launch(Dispatchers.IO) {
try {
awaitCallback<Unit> {
room.unban(roomMemberSummary.userId, null, it)
}
} catch (failure: Throwable) {
_viewEvents.post(RoomBannedViewEvents.ToastError(stringProvider.getString(R.string.failed_to_unban)))
} finally {
setState {
copy(
onGoingModerationAction = onGoingModerationAction - roomMemberSummary.userId
)
}
}
}
}
}

View File

@ -0,0 +1,92 @@
/*
* Copyright (c) 2020 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.roomprofile.banned
import com.airbnb.epoxy.TypedEpoxyController
import im.vector.matrix.android.api.session.room.model.RoomMemberSummary
import im.vector.matrix.android.api.util.toMatrixItem
import im.vector.riotx.R
import im.vector.riotx.core.epoxy.dividerItem
import im.vector.riotx.core.epoxy.profiles.buildProfileSection
import im.vector.riotx.core.epoxy.profiles.profileMatrixItemWithProgress
import im.vector.riotx.core.extensions.join
import im.vector.riotx.core.resources.ColorProvider
import im.vector.riotx.core.resources.StringProvider
import im.vector.riotx.core.ui.list.genericFooterItem
import im.vector.riotx.features.home.AvatarRenderer
import javax.inject.Inject
class RoomBannedMemberListController @Inject constructor(
private val avatarRenderer: AvatarRenderer,
private val stringProvider: StringProvider,
colorProvider: ColorProvider
) : TypedEpoxyController<RoomBannedMemberListViewState>() {
interface Callback {
fun onUnbanClicked(roomMember: RoomMemberSummary)
}
private val dividerColor = colorProvider.getColorFromAttribute(R.attr.vctr_list_divider_color)
var callback: Callback? = null
init {
setData(null)
}
override fun buildModels(data: RoomBannedMemberListViewState?) {
val bannedList = data?.bannedMemberSummaries?.invoke() ?: return
buildProfileSection(
stringProvider.getString(R.string.room_settings_banned_users_title)
)
bannedList.join(
each = { _, roomMember ->
val actionInProgress = data.onGoingModerationAction.contains(roomMember.userId)
profileMatrixItemWithProgress {
id(roomMember.userId)
matrixItem(roomMember.toMatrixItem())
avatarRenderer(avatarRenderer)
apply {
if (actionInProgress) {
inProgress(true)
editable(false)
} else {
inProgress(false)
editable(true)
clickListener { _ ->
callback?.onUnbanClicked(roomMember)
}
}
}
}
},
between = { _, roomMemberBefore ->
dividerItem {
id("divider_${roomMemberBefore.userId}")
color(dividerColor)
}
}
)
genericFooterItem {
id("footer")
text(stringProvider.getQuantityString(R.plurals.room_settings_banned_users_count, bannedList.size, bannedList.size))
}
}
}

View File

@ -0,0 +1,98 @@
/*
* Copyright (c) 2020 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.roomprofile.banned
import android.os.Bundle
import android.view.View
import androidx.appcompat.app.AlertDialog
import com.airbnb.mvrx.args
import com.airbnb.mvrx.fragmentViewModel
import com.airbnb.mvrx.withState
import im.vector.matrix.android.api.session.room.model.RoomMemberSummary
import im.vector.matrix.android.api.util.toMatrixItem
import im.vector.riotx.R
import im.vector.riotx.core.extensions.cleanup
import im.vector.riotx.core.extensions.configureWith
import im.vector.riotx.core.platform.VectorBaseFragment
import im.vector.riotx.core.utils.toast
import im.vector.riotx.features.home.AvatarRenderer
import im.vector.riotx.features.roomprofile.RoomProfileArgs
import kotlinx.android.synthetic.main.fragment_room_setting_generic.*
import javax.inject.Inject
class RoomBannedMemberListFragment @Inject constructor(
val viewModelFactory: RoomBannedListMemberViewModel.Factory,
private val roomMemberListController: RoomBannedMemberListController,
private val avatarRenderer: AvatarRenderer
) : VectorBaseFragment(), RoomBannedMemberListController.Callback {
private val viewModel: RoomBannedListMemberViewModel by fragmentViewModel()
private val roomProfileArgs: RoomProfileArgs by args()
override fun getLayoutResId() = R.layout.fragment_room_setting_generic
override fun onUnbanClicked(roomMember: RoomMemberSummary) {
viewModel.handle(RoomBannedListMemberAction.QueryInfo(roomMember))
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
roomMemberListController.callback = this
setupToolbar(roomSettingsToolbar)
recyclerView.configureWith(roomMemberListController, hasFixedSize = true)
viewModel.observeViewEvents {
when (it) {
is RoomBannedViewEvents.ShowBannedInfo -> {
val canBan = withState(viewModel) { state -> state.canUserBan }
AlertDialog.Builder(requireActivity())
.setTitle(getString(R.string.member_banned_by, it.bannedByUserId))
.setMessage(getString(R.string.reason_colon, it.banReason))
.setPositiveButton(R.string.ok, null)
.apply {
if (canBan) {
setNegativeButton(R.string.room_participants_action_unban) { _, _ ->
viewModel.handle(RoomBannedListMemberAction.UnBanUser(it.roomMemberSummary))
}
}
}
.show()
}
is RoomBannedViewEvents.ToastError -> {
requireActivity().toast(it.info)
}
}
}
}
override fun onDestroyView() {
recyclerView.cleanup()
super.onDestroyView()
}
override fun invalidate() = withState(viewModel) { viewState ->
roomMemberListController.setData(viewState)
renderRoomSummary(viewState)
}
private fun renderRoomSummary(state: RoomBannedMemberListViewState) {
state.roomSummary()?.let {
roomSettingsToolbarTitleView.text = it.displayName
avatarRenderer.render(it.toMatrixItem(), roomSettingsToolbarAvatarImageView)
}
}
}

View File

@ -0,0 +1,35 @@
/*
* Copyright (c) 2020 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.roomprofile.banned
import com.airbnb.mvrx.Async
import com.airbnb.mvrx.MvRxState
import com.airbnb.mvrx.Uninitialized
import im.vector.matrix.android.api.session.room.model.RoomMemberSummary
import im.vector.matrix.android.api.session.room.model.RoomSummary
import im.vector.riotx.features.roomprofile.RoomProfileArgs
data class RoomBannedMemberListViewState(
val roomId: String,
val roomSummary: Async<RoomSummary> = Uninitialized,
val bannedMemberSummaries: Async<List<RoomMemberSummary>> = Uninitialized,
val onGoingModerationAction: List<String> = emptyList(),
val canUserBan: Boolean = false
) : MvRxState {
constructor(args: RoomProfileArgs) : this(roomId = args.roomId)
}

View File

@ -0,0 +1,25 @@
/*
* Copyright (c) 2020 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.roomprofile.banned
import im.vector.matrix.android.api.session.room.model.RoomMemberSummary
import im.vector.riotx.core.platform.VectorViewEvents
sealed class RoomBannedViewEvents : VectorViewEvents {
data class ShowBannedInfo(val bannedByUserId: String, val banReason: String, val roomMemberSummary: RoomMemberSummary) : RoomBannedViewEvents()
data class ToastError(val info: String) : RoomBannedViewEvents()
}

View File

@ -28,7 +28,6 @@ import im.vector.riotx.core.resources.StringProvider
import im.vector.riotx.features.login.LoginMode
import im.vector.riotx.features.signout.soft.epoxy.loginCenterButtonItem
import im.vector.riotx.features.signout.soft.epoxy.loginErrorWithRetryItem
import im.vector.riotx.features.signout.soft.epoxy.loginHeaderItem
import im.vector.riotx.features.signout.soft.epoxy.loginPasswordFormItem
import im.vector.riotx.features.signout.soft.epoxy.loginRedButtonItem
import im.vector.riotx.features.signout.soft.epoxy.loginTextItem
@ -65,9 +64,6 @@ class SoftLogoutController @Inject constructor(
}
private fun buildHeader(state: SoftLogoutViewState) {
loginHeaderItem {
id("header")
}
loginTitleItem {
id("title")
text(stringProvider.getString(R.string.soft_logout_title))

View File

@ -1,27 +0,0 @@
/*
* 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.signout.soft.epoxy
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_login_header)
abstract class LoginHeaderItem : VectorEpoxyModel<LoginHeaderItem.Holder>() {
class Holder : VectorEpoxyHolder()
}

View File

@ -1,27 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="155dp"
android:height="33dp"
android:viewportWidth="155"
android:viewportHeight="33">
<path
android:pathData="M21.533,22.855H4.969C5.165,24.595 5.794,25.985 6.856,27.023C7.918,28.034 9.316,28.539 11.05,28.539C12.196,28.539 13.23,28.258 14.153,27.697C15.075,27.135 15.732,26.378 16.124,25.423H21.156C20.485,27.641 19.227,29.437 17.382,30.812C15.564,32.16 13.412,32.833 10.924,32.833C7.681,32.833 5.053,31.753 3.04,29.591C1.055,27.43 0.063,24.694 0.063,21.381C0.063,18.153 1.069,15.445 3.082,13.255C5.095,11.066 7.695,9.972 10.882,9.972C14.069,9.972 16.641,11.052 18.598,13.213C20.583,15.347 21.575,18.041 21.575,21.297L21.533,22.855ZM10.882,14.056C9.316,14.056 8.016,14.519 6.982,15.445C5.947,16.371 5.304,17.606 5.053,19.15H16.627C16.403,17.606 15.788,16.371 14.782,15.445C13.775,14.519 12.475,14.056 10.882,14.056Z"
android:fillColor="#ffffff"/>
<path
android:pathData="M25.009,25.802V0.751H30V25.886C30,27.009 30.615,27.57 31.845,27.57L32.725,27.528V32.286C32.25,32.37 31.747,32.412 31.216,32.412C29.063,32.412 27.483,31.865 26.477,30.77C25.499,29.676 25.009,28.02 25.009,25.802Z"
android:fillColor="#ffffff"/>
<path
android:pathData="M55.966,22.855H39.401C39.597,24.595 40.226,25.985 41.289,27.023C42.351,28.034 43.749,28.539 45.482,28.539C46.628,28.539 47.663,28.258 48.585,27.697C49.508,27.135 50.165,26.378 50.556,25.423H55.588C54.917,27.641 53.659,29.437 51.814,30.812C49.997,32.16 47.844,32.833 45.356,32.833C42.113,32.833 39.485,31.753 37.472,29.591C35.487,27.43 34.495,24.694 34.495,21.381C34.495,18.153 35.501,15.445 37.514,13.255C39.527,11.066 42.127,9.972 45.314,9.972C48.501,9.972 51.073,11.052 53.03,13.213C55.015,15.347 56.008,18.041 56.008,21.297L55.966,22.855ZM45.314,14.056C43.749,14.056 42.449,14.519 41.414,15.445C40.38,16.371 39.737,17.606 39.485,19.15H51.059C50.836,17.606 50.221,16.371 49.214,15.445C48.208,14.519 46.908,14.056 45.314,14.056Z"
android:fillColor="#ffffff"/>
<path
android:pathData="M78.48,19.781V32.328H73.49V19.234C73.49,15.922 72.12,14.266 69.38,14.266C67.899,14.266 66.711,14.743 65.816,15.698C64.949,16.652 64.516,17.957 64.516,19.613V32.328H59.526V10.477H64.138V13.382C64.67,12.399 65.48,11.585 66.571,10.94C67.661,10.294 69.017,9.972 70.638,9.972C73.658,9.972 75.838,11.122 77.18,13.424C79.025,11.122 81.486,9.972 84.561,9.972C87.105,9.972 89.062,10.771 90.432,12.371C91.802,13.943 92.487,16.02 92.487,18.603V32.328H87.496V19.234C87.496,15.922 86.126,14.266 83.387,14.266C81.877,14.266 80.675,14.757 79.78,15.74C78.914,16.694 78.48,18.041 78.48,19.781Z"
android:fillColor="#ffffff"/>
<path
android:pathData="M117.304,22.855H100.739C100.935,24.595 101.564,25.985 102.627,27.023C103.689,28.034 105.087,28.539 106.82,28.539C107.966,28.539 109.001,28.258 109.923,27.697C110.846,27.135 111.503,26.378 111.894,25.423H116.926C116.255,27.641 114.997,29.437 113.152,30.812C111.335,32.16 109.182,32.833 106.694,32.833C103.451,32.833 100.823,31.753 98.811,29.591C96.826,27.43 95.833,24.694 95.833,21.381C95.833,18.153 96.84,15.445 98.852,13.255C100.865,11.066 103.465,9.972 106.652,9.972C109.839,9.972 112.411,11.052 114.368,13.213C116.353,15.347 117.346,18.041 117.346,21.297L117.304,22.855ZM106.652,14.056C105.087,14.056 103.787,14.519 102.752,15.445C101.718,16.371 101.075,17.606 100.823,19.15H112.397C112.174,17.606 111.559,16.371 110.552,15.445C109.546,14.519 108.246,14.056 106.652,14.056Z"
android:fillColor="#ffffff"/>
<path
android:pathData="M125.477,10.477V13.382C125.98,12.427 126.804,11.628 127.951,10.982C129.125,10.308 130.537,9.972 132.186,9.972C134.758,9.972 136.743,10.757 138.141,12.329C139.567,13.901 140.28,15.992 140.28,18.603V32.328H135.289V19.234C135.289,17.69 134.926,16.483 134.199,15.613C133.5,14.715 132.424,14.266 130.97,14.266C129.376,14.266 128.118,14.743 127.196,15.698C126.301,16.652 125.854,17.971 125.854,19.655V32.328H120.864V10.477H125.477Z"
android:fillColor="#ffffff"/>
<path
android:pathData="M154.854,27.865V32.202C154.239,32.37 153.372,32.454 152.254,32.454C148.004,32.454 145.88,30.307 145.88,26.013V14.476H142.567V10.477H145.88V4.793H150.87V10.477H154.938V14.476H150.87V25.507C150.87,27.22 151.681,28.076 153.302,28.076L154.854,27.865Z"
android:fillColor="#ffffff"/>
</vector>

View File

@ -0,0 +1,43 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="369dp"
android:height="211dp"
android:viewportWidth="369"
android:viewportHeight="211">
<path
android:pathData="M51.155,186.5H11.69C12.156,190.633 13.655,193.933 16.186,196.4C18.717,198.8 22.047,200 26.177,200C28.908,200 31.373,199.333 33.571,198C35.769,196.667 37.334,194.867 38.267,192.6H50.256C48.658,197.867 45.66,202.133 41.264,205.4C36.934,208.6 31.806,210.2 25.877,210.2C18.151,210.2 11.89,207.633 7.094,202.5C2.365,197.367 0,190.867 0,183C0,175.333 2.398,168.9 7.194,163.7C11.99,158.5 18.184,155.9 25.778,155.9C33.371,155.9 39.499,158.467 44.161,163.6C48.891,168.667 51.255,175.067 51.255,182.8L51.155,186.5ZM25.778,165.6C22.047,165.6 18.95,166.7 16.486,168.9C14.021,171.1 12.489,174.033 11.89,177.7H39.466C38.933,174.033 37.467,171.1 35.069,168.9C32.672,166.7 29.574,165.6 25.778,165.6Z"
android:fillColor="#ffffff"/>
<path
android:pathData="M59.437,193.5V134H71.327V193.7C71.327,196.367 72.792,197.7 75.723,197.7L77.821,197.6V208.9C76.689,209.1 75.49,209.2 74.224,209.2C69.095,209.2 65.332,207.9 62.934,205.3C60.603,202.7 59.437,198.767 59.437,193.5Z"
android:fillColor="#ffffff"/>
<path
android:pathData="M133.193,186.5H93.728C94.194,190.633 95.693,193.933 98.224,196.4C100.755,198.8 104.085,200 108.215,200C110.946,200 113.41,199.333 115.609,198C117.807,196.667 119.372,194.867 120.304,192.6H132.294C130.695,197.867 127.698,202.133 123.302,205.4C118.972,208.6 113.843,210.2 107.915,210.2C100.189,210.2 93.927,207.633 89.132,202.5C84.402,197.367 82.038,190.867 82.038,183C82.038,175.333 84.436,168.9 89.231,163.7C94.027,158.5 100.222,155.9 107.815,155.9C115.409,155.9 121.537,158.467 126.199,163.6C130.928,168.667 133.293,175.067 133.293,182.8L133.193,186.5ZM107.815,165.6C104.085,165.6 100.988,166.7 98.523,168.9C96.059,171.1 94.527,174.033 93.927,177.7H121.503C120.971,174.033 119.505,171.1 117.107,168.9C114.709,166.7 111.612,165.6 107.815,165.6Z"
android:fillColor="#ffffff"/>
<path
android:pathData="M186.835,179.2V209H174.946V177.9C174.946,170.033 171.682,166.1 165.154,166.1C161.624,166.1 158.793,167.233 156.662,169.5C154.597,171.767 153.564,174.867 153.564,178.8V209H141.675V157.1H152.665V164C153.931,161.667 155.862,159.733 158.46,158.2C161.058,156.667 164.288,155.9 168.152,155.9C175.345,155.9 180.541,158.633 183.738,164.1C188.134,158.633 193.996,155.9 201.323,155.9C207.384,155.9 212.047,157.8 215.311,161.6C218.574,165.333 220.206,170.267 220.206,176.4V209H208.317V177.9C208.317,170.033 205.053,166.1 198.525,166.1C194.928,166.1 192.064,167.267 189.933,169.6C187.868,171.867 186.835,175.067 186.835,179.2Z"
android:fillColor="#ffffff"/>
<path
android:pathData="M279.335,186.5H239.87C240.336,190.633 241.835,193.933 244.366,196.4C246.897,198.8 250.227,200 254.357,200C257.088,200 259.552,199.333 261.751,198C263.949,196.667 265.514,194.867 266.447,192.6H278.436C276.837,197.867 273.84,202.133 269.444,205.4C265.114,208.6 259.985,210.2 254.057,210.2C246.331,210.2 240.069,207.633 235.274,202.5C230.544,197.367 228.18,190.867 228.18,183C228.18,175.333 230.578,168.9 235.374,163.7C240.169,158.5 246.364,155.9 253.957,155.9C261.551,155.9 267.679,158.467 272.341,163.6C277.071,168.667 279.435,175.067 279.435,182.8L279.335,186.5ZM253.957,165.6C250.227,165.6 247.13,166.7 244.665,168.9C242.201,171.1 240.669,174.033 240.069,177.7H267.645C267.113,174.033 265.647,171.1 263.249,168.9C260.851,166.7 257.754,165.6 253.957,165.6Z"
android:fillColor="#ffffff"/>
<path
android:pathData="M298.807,157.1V164C300.006,161.733 301.971,159.833 304.702,158.3C307.5,156.7 310.863,155.9 314.793,155.9C320.921,155.9 325.651,157.767 328.981,161.5C332.378,165.233 334.077,170.2 334.077,176.4V209H322.187V177.9C322.187,174.233 321.321,171.367 319.589,169.3C317.924,167.167 315.36,166.1 311.896,166.1C308.099,166.1 305.102,167.233 302.904,169.5C300.772,171.767 299.707,174.9 299.707,178.9V209H287.817V157.1H298.807Z"
android:fillColor="#ffffff"/>
<path
android:pathData="M368.8,198.4V208.7C367.335,209.1 365.27,209.3 362.606,209.3C352.481,209.3 347.419,204.2 347.419,194V166.6H339.526V157.1H347.419V143.6H359.308V157.1H369V166.6H359.308V192.8C359.308,196.867 361.24,198.9 365.103,198.9L368.8,198.4Z"
android:fillColor="#ffffff"/>
<path
android:pathData="M171,6C171,2.686 173.686,0 177,0C199.091,0 217,17.909 217,40C217,43.314 214.314,46 211,46C207.686,46 205,43.314 205,40C205,24.536 192.464,12 177,12C173.686,12 171,9.314 171,6Z"
android:fillColor="#ffffff"
android:fillType="evenOdd"/>
<path
android:pathData="M199,94C199,97.314 196.314,100 193,100C170.909,100 153,82.091 153,60C153,56.686 155.686,54 159,54C162.314,54 165,56.686 165,60C165,75.464 177.536,88 193,88C196.314,88 199,90.686 199,94Z"
android:fillColor="#ffffff"
android:fillType="evenOdd"/>
<path
android:pathData="M141,64C137.686,64 135,61.314 135,58C135,35.909 152.909,18 175,18C178.314,18 181,20.686 181,24C181,27.314 178.314,30 175,30C159.536,30 147,42.536 147,58C147,61.314 144.314,64 141,64Z"
android:fillColor="#ffffff"
android:fillType="evenOdd"/>
<path
android:pathData="M229,36C232.314,36 235,38.686 235,42C235,64.091 217.091,82 195,82C191.686,82 189,79.314 189,76C189,72.686 191.686,70 195,70C210.464,70 223,57.464 223,42C223,38.686 225.686,36 229,36Z"
android:fillColor="#ffffff"
android:fillType="evenOdd"/>
</vector>

View File

@ -1,22 +1,20 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="21dp"
android:height="22dp"
android:viewportWidth="21"
android:viewportHeight="22">
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M9.4969,3.0606L2.8882,3.0606C1.8454,3.0606 1,3.9289 1,5L1,18.5758C1,19.6469 1.8454,20.5152 2.8882,20.5152L16.1056,20.5152C17.1484,20.5152 17.9938,19.6469 17.9938,18.5758L17.9938,11.7879"
android:pathData="M4,18.6666L20,18.6666"
android:strokeLineJoin="round"
android:strokeWidth="2"
android:fillColor="#00000000"
android:strokeColor="#9E9E9E"
android:fillType="evenOdd"
android:strokeColor="#000000"
android:strokeLineCap="round"/>
<path
android:pathData="M16.5776,1.6061C17.3598,0.8027 18.6278,0.8027 19.4099,1.6061C20.1921,2.4094 20.1921,3.7118 19.4099,4.5152L10.441,13.7273L6.6646,14.697L7.6087,10.8182L16.5776,1.6061Z"
android:pathData="M6.6667,14L16.0001,4.6666"
android:strokeLineJoin="round"
android:strokeWidth="2"
android:fillColor="#00000000"
android:strokeColor="#9E9E9E"
android:fillType="evenOdd"
android:strokeColor="#000000"
android:strokeLineCap="round"/>
</vector>

View File

@ -6,11 +6,6 @@
android:layout_height="match_parent"
android:background="?riotx_background">
<!-- Missing attributes are in the style -->
<ImageView
style="@style/LoginLogo"
tools:ignore="ContentDescription,MissingConstraints" />
<!-- Missing attributes are in the style -->
<androidx.core.widget.NestedScrollView
style="@style/LoginFormScrollView"

View File

@ -7,11 +7,6 @@
android:layout_height="match_parent"
android:background="?riotx_background">
<!-- Missing attributes are in the style -->
<ImageView
style="@style/LoginLogo"
tools:ignore="ContentDescription,MissingConstraints" />
<!-- Missing attributes are in the style -->
<androidx.core.widget.NestedScrollView
style="@style/LoginFormScrollView"

View File

@ -6,11 +6,6 @@
android:layout_height="match_parent"
android:background="?riotx_background">
<!-- Missing attributes are in the style -->
<ImageView
style="@style/LoginLogo"
tools:ignore="ContentDescription,MissingConstraints" />
<!-- Missing attributes are in the style -->
<!-- No scroll view in the screen, but use the style -->
<LinearLayout

View File

@ -7,11 +7,6 @@
android:layout_height="match_parent"
android:background="?riotx_background">
<!-- Missing attributes are in the style -->
<ImageView
style="@style/LoginLogo"
tools:ignore="ContentDescription,MissingConstraints" />
<!-- Missing attributes are in the style -->
<androidx.core.widget.NestedScrollView
style="@style/LoginFormScrollView"

View File

@ -6,11 +6,6 @@
android:layout_height="match_parent"
android:background="?riotx_background">
<!-- Missing attributes are in the style -->
<ImageView
style="@style/LoginLogo"
tools:ignore="ContentDescription,MissingConstraints" />
<!-- Missing attributes are in the style -->
<androidx.core.widget.NestedScrollView
style="@style/LoginFormScrollView"

View File

@ -5,11 +5,6 @@
android:layout_height="match_parent"
android:background="?riotx_background">
<!-- Missing attributes are in the style -->
<ImageView
style="@style/LoginLogo"
tools:ignore="ContentDescription,MissingConstraints" />
<!-- Missing attributes are in the style -->
<androidx.core.widget.NestedScrollView
style="@style/LoginFormScrollView"

View File

@ -5,11 +5,6 @@
android:layout_height="match_parent"
android:background="?riotx_background">
<!-- Missing attributes are in the style -->
<ImageView
style="@style/LoginLogo"
tools:ignore="ContentDescription,MissingConstraints" />
<!-- Missing attributes are in the style -->
<androidx.core.widget.NestedScrollView
style="@style/LoginFormScrollView"

View File

@ -6,11 +6,6 @@
android:layout_height="match_parent"
android:background="?riotx_background">
<!-- Missing attributes are in the style -->
<ImageView
style="@style/LoginLogo"
tools:ignore="ContentDescription,MissingConstraints,UnusedAttribute" />
<!-- Missing attributes are in the style -->
<androidx.core.widget.NestedScrollView
style="@style/LoginFormScrollView"

View File

@ -7,11 +7,6 @@
android:layout_height="match_parent"
android:background="?riotx_background">
<!-- Missing attributes are in the style -->
<ImageView
style="@style/LoginLogo"
tools:ignore="ContentDescription,MissingConstraints" />
<!-- Missing attributes are in the style -->
<androidx.core.widget.NestedScrollView
style="@style/LoginFormScrollView"

View File

@ -6,11 +6,6 @@
android:layout_height="match_parent"
android:background="?riotx_background">
<!-- Missing attributes are in the style -->
<ImageView
style="@style/LoginLogo"
tools:ignore="ContentDescription,MissingConstraints" />
<!-- Missing attributes are in the style -->
<androidx.core.widget.NestedScrollView
style="@style/LoginFormScrollView"

View File

@ -18,28 +18,17 @@
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/loginSplashLogo"
android:layout_width="64dp"
android:layout_height="64dp"
android:src="@drawable/element_logo_green"
android:transitionName="loginLogoTransition"
app:layout_constraintBottom_toTopOf="@+id/logoType"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_chainStyle="packed" />
<ImageView
android:id="@+id/logoType"
android:layout_width="wrap_content"
android:layout_height="44dp"
android:layout_marginTop="8dp"
android:src="@drawable/element_logotype"
android:layout_height="102dp"
android:src="@drawable/element_logotype_combined"
android:tint="?colorAccent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/loginSplashLogo" />
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_chainStyle="packed" />
<TextView
android:id="@+id/loginSplashTitle"

View File

@ -6,11 +6,6 @@
android:layout_height="match_parent"
android:background="?riotx_background">
<!-- Missing attributes are in the style -->
<ImageView
style="@style/LoginLogo"
tools:ignore="ContentDescription,MissingConstraints" />
<!-- Missing attributes are in the style -->
<androidx.constraintlayout.widget.ConstraintLayout
style="@style/LoginFormScrollView"

View File

@ -6,11 +6,6 @@
android:layout_height="match_parent"
android:background="?riotx_background">
<!-- Missing attributes are in the style -->
<ImageView
style="@style/LoginLogo"
tools:ignore="ContentDescription,MissingConstraints" />
<!-- Missing attributes are in the style -->
<androidx.core.widget.NestedScrollView
style="@style/LoginFormScrollView"

View File

@ -3,6 +3,7 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="1dp"
android:background="?riotx_background"
android:orientation="vertical">

View File

@ -1,9 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<ImageView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@id/loginLogo"
android:layout_width="match_parent"
android:layout_height="60dp"
android:layout_gravity="center_horizontal"
android:layout_marginTop="32dp"
android:importantForAccessibility="no"
android:src="@drawable/element_logo_green" />

View File

@ -0,0 +1,102 @@
<?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:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?riotx_background"
android:clickable="true"
android:focusable="true"
android:foreground="?attr/selectableItemBackground"
android:minHeight="64dp"
android:paddingLeft="@dimen/layout_horizontal_margin"
android:paddingTop="8dp"
android:paddingRight="@dimen/layout_horizontal_margin"
android:paddingBottom="8dp">
<ImageView
android:id="@+id/matrixItemAvatar"
android:layout_width="32dp"
android:layout_height="32dp"
android:layout_centerVertical="true"
android:scaleType="centerInside"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:src="@tools:sample/avatars" />
<ImageView
android:id="@+id/matrixItemAvatarDecoration"
android:layout_width="20dp"
android:layout_height="20dp"
app:layout_constraintCircle="@+id/matrixItemAvatar"
app:layout_constraintCircleAngle="135"
app:layout_constraintCircleRadius="16dp"
tools:ignore="MissingConstraints"
tools:src="@drawable/ic_shield_trusted" />
<TextView
android:id="@+id/matrixItemTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:drawablePadding="16dp"
android:ellipsize="end"
android:maxLines="1"
android:textColor="?riotx_text_primary"
android:textSize="16sp"
app:layout_constrainedWidth="true"
app:layout_constraintBottom_toTopOf="@+id/matrixItemSubtitle"
app:layout_constraintEnd_toStartOf="@+id/matrixItemProgress"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toEndOf="@id/matrixItemAvatar"
app:layout_constraintTop_toTopOf="parent"
app:layout_goneMarginStart="0dp"
tools:text="@sample/matrix.json/data/displayName" />
<TextView
android:id="@+id/matrixItemSubtitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:drawablePadding="16dp"
android:ellipsize="end"
android:maxLines="1"
android:textColor="?riotx_text_secondary"
android:textSize="12sp"
app:layout_constrainedWidth="true"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/matrixItemProgress"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toEndOf="@id/matrixItemAvatar"
app:layout_constraintTop_toBottomOf="@id/matrixItemTitle"
app:layout_goneMarginStart="0dp"
tools:text="@sample/matrix.json/data/mxid" />
<ProgressBar
android:id="@+id/matrixItemProgress"
style="?android:attr/progressBarStyle"
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_marginEnd="8dp"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@id/matrixItemEditable"
app:layout_constraintTop_toTopOf="parent"
tools:visibility="visible" />
<ImageView
android:id="@+id/matrixItemEditable"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_arrow_right"
android:tint="?riotx_text_secondary"
android:visibility="visible"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:visibility="visible" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -1,19 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- We will improve this later
<style name="LoginLogo" parent="LoginLogoBase">
<item name="layout_constraintEnd_toStartOf">@id/loginFormContainer</item>
<item name="layout_constraintBottom_toBottomOf">parent</item>
<item name="android:layout_height">0dp</item>
<item name="layout_constraintHorizontal_chainStyle">packed</item>
</style>
<style name="LoginFormContainer" parent="LoginFormContainerBase">
<item name="layout_constraintWidth_percent">0.6</item>
<item name="layout_constraintStart_toEndOf">@id/loginLogo</item>
<item name="layout_constraintTop_toTopOf">parent</item>
</style>
-->
</resources>

View File

@ -1,8 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="LoginLogo.v21" parent="LoginLogoBase">
<item name="android:transitionName">loginLogoTransition</item>
</style>
</resources>

View File

@ -992,6 +992,10 @@
<!-- Room settings: banned users -->
<string name="room_settings_banned_users_title">Banned users</string>
<plurals name="room_settings_banned_users_count">
<item quantity="one">1 banned user</item>
<item quantity="other">%d banned users</item>
</plurals>
<!-- advanced -->
<string name="room_settings_category_advanced_title">Advanced</string>
@ -2499,6 +2503,7 @@ Not all features in Riot are implemented in Element yet. Main missing (and comin
<string name="identity_server_set_alternative_notice_no_default">Enter the URL of an identity server</string>
<string name="identity_server_set_alternative_submit">Submit</string>
<string name="power_level_edit_title">Set role</string>
<string name="power_level_title">Role</string>
<string name="a11y_open_chat">Open chat</string>
<string name="a11y_mute_microphone">Mute the microphone</string>
<string name="a11y_unmute_microphone">Unmute the microphone</string>
@ -2560,4 +2565,7 @@ Not all features in Riot are implemented in Element yet. Main missing (and comin
<string name="three_pid_revoke_invite_dialog_title">Revoke invite</string>
<string name="three_pid_revoke_invite_dialog_content">Revoke invite to %1$s?</string>
<string name="member_banned_by">Banned by %1$s</string>
<string name="failed_to_unban">Failed to UnBan user</string>
</resources>

View File

@ -8,27 +8,9 @@
<item name="android:paddingEnd">36dp</item>
</style>
<item name="loginLogo" type="id" />
<item name="loginFormScrollView" type="id" />
<item name="loginFormContainer" type="id" />
<style name="LoginLogoBase">
<item name="android:id">@id/loginLogo</item>
<item name="android:layout_width">60dp</item>
<item name="android:layout_height">60dp</item>
<item name="android:src">@drawable/element_logo_green</item>
<item name="android:importantForAccessibility">no</item>
<item name="layout_constraintTop_toTopOf">parent</item>
<item name="layout_constraintStart_toStartOf">parent</item>
</style>
<style name="LoginLogo.v21" parent="LoginLogoBase" />
<style name="LoginLogo" parent="LoginLogo.v21">
<item name="layout_constraintEnd_toEndOf">parent</item>
<item name="android:layout_marginTop">48dp</item>
</style>
<style name="LoginFormContainer">
<item name="android:id">@id/loginFormContainer</item>
<item name="android:paddingStart">36dp</item>
@ -49,10 +31,10 @@
</style>
<style name="LoginFormScrollView" parent="LoginFormScrollViewBase">
<item name="layout_constraintTop_toBottomOf">@id/loginLogo</item>
<item name="layout_constraintTop_toTopOf">parent</item>
<item name="layout_constraintStart_toStartOf">parent</item>
<item name="android:layout_height">0dp</item>
<item name="android:layout_marginTop">24dp</item>
<item name="android:layout_marginTop">72dp</item>
</style>
<style name="Style.Vector.Login.Button" parent="VectorButtonStyle">

View File

@ -24,6 +24,7 @@
app:fragment="im.vector.riotx.features.settings.VectorSettingsPreferencesFragment" />
<im.vector.riotx.core.preference.VectorPreference
app:isPreferenceVisible="@bool/false_not_implemented"
android:enabled="@bool/false_not_implemented"
android:icon="@drawable/ic_settings_root_call"
android:title="@string/preference_voice_and_video"