diff --git a/CHANGES.md b/CHANGES.md index a028ef6f1e..579124ce45 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -2,6 +2,7 @@ Changes in Element 1.0.14 (2020-XX-XX) =================================================== Features ✨: + - Allow changing nick colors (#2610) - Enable url previews for notices (#2562) Improvements 🙌: diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/accountdata/UserAccountDataTypes.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/accountdata/UserAccountDataTypes.kt index 69b15ff7d4..91167d896f 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/accountdata/UserAccountDataTypes.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/accountdata/UserAccountDataTypes.kt @@ -27,4 +27,5 @@ object UserAccountDataTypes { const val TYPE_ALLOWED_WIDGETS = "im.vector.setting.allowed_widgets" const val TYPE_IDENTITY_SERVER = "m.identity_server" const val TYPE_ACCEPTED_TERMS = "m.accepted_terms" + const val TYPE_OVERRIDE_COLORS = "im.vector.setting.override_colors" } diff --git a/vector/src/main/java/im/vector/app/AppStateHandler.kt b/vector/src/main/java/im/vector/app/AppStateHandler.kt index 1e92f7bc67..b2791c0978 100644 --- a/vector/src/main/java/im/vector/app/AppStateHandler.kt +++ b/vector/src/main/java/im/vector/app/AppStateHandler.kt @@ -23,12 +23,15 @@ import arrow.core.Option import im.vector.app.features.grouplist.ALL_COMMUNITIES_GROUP_ID import im.vector.app.features.grouplist.SelectedGroupDataSource import im.vector.app.features.home.HomeRoomListDataSource +import im.vector.app.features.home.room.detail.timeline.helper.MatrixItemColorProvider import im.vector.app.features.home.room.list.ChronologicalRoomComparator import io.reactivex.Observable import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.disposables.CompositeDisposable import io.reactivex.functions.BiFunction import io.reactivex.rxkotlin.addTo +import org.matrix.android.sdk.api.session.accountdata.UserAccountDataTypes +import org.matrix.android.sdk.api.session.events.model.toModel import org.matrix.android.sdk.api.session.group.model.GroupSummary import org.matrix.android.sdk.api.session.room.model.RoomSummary import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams @@ -46,13 +49,15 @@ class AppStateHandler @Inject constructor( private val sessionDataSource: ActiveSessionDataSource, private val homeRoomListDataSource: HomeRoomListDataSource, private val selectedGroupDataSource: SelectedGroupDataSource, - private val chronologicalRoomComparator: ChronologicalRoomComparator) : LifecycleObserver { + private val chronologicalRoomComparator: ChronologicalRoomComparator, + private val matrixItemColorProvider: MatrixItemColorProvider) : LifecycleObserver { private val compositeDisposable = CompositeDisposable() @OnLifecycleEvent(Lifecycle.Event.ON_RESUME) fun entersForeground() { observeRoomsAndGroup() + observeUserAccountData() } @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE) @@ -93,4 +98,19 @@ class AppStateHandler @Inject constructor( } .addTo(compositeDisposable) } + + private fun observeUserAccountData() { + sessionDataSource.observe() + .observeOn(AndroidSchedulers.mainThread()) + .switchMap { + it.orNull()?.rx()?.liveAccountData(setOf(UserAccountDataTypes.TYPE_OVERRIDE_COLORS)) + ?: Observable.just(emptyList()) + } + .distinctUntilChanged() + .subscribe { + val overrideColorSpecs = it?.first()?.content?.toModel>() + matrixItemColorProvider.setOverrideColors(overrideColorSpecs) + } + .addTo(compositeDisposable) + } } diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/MatrixItemColorProvider.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/MatrixItemColorProvider.kt index 6a590206cb..dcbfb13dcc 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/MatrixItemColorProvider.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/MatrixItemColorProvider.kt @@ -44,6 +44,39 @@ class MatrixItemColorProvider @Inject constructor( } } + fun setOverrideColors(overrideColors: Map?) { + overrideColors?.forEach() { + setOverrideColor(it.key, it.value) + } + } + + fun setOverrideColor(id: String, colorSpec: String?) : Boolean { + val color = parseUserColorSpec(colorSpec) + if (color == null) { + cache.remove(id) + return false + } else { + cache.put(id, color) + return true + } + } + + @ColorInt + private fun parseUserColorSpec(colorText: String?): Int? { + if (colorText.isNullOrBlank()) { + return null + } + try { + if (colorText.first() == '#') { + return (colorText.substring(1).toLong(radix = 16) or 0xff000000L).toInt() + } else { + return colorProvider.getColor(getUserColorByIndex(colorText.toInt())) + } + } catch (e: Throwable) { + return null + } + } + companion object { @ColorRes @VisibleForTesting @@ -52,7 +85,12 @@ class MatrixItemColorProvider @Inject constructor( userId?.toList()?.map { chr -> hash = (hash shl 5) - hash + chr.toInt() } - return when (abs(hash) % 8) { + return getUserColorByIndex(abs(hash)) + } + + @ColorRes + private fun getUserColorByIndex(index: Int): Int { + return when (index % 8) { 1 -> R.color.riotx_username_2 2 -> R.color.riotx_username_3 3 -> R.color.riotx_username_4 diff --git a/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileFragment.kt b/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileFragment.kt index 08a251834e..3c0ff15fb8 100644 --- a/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileFragment.kt +++ b/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileFragment.kt @@ -43,6 +43,7 @@ import im.vector.app.core.extensions.setTextOrHide import im.vector.app.core.platform.StateView import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.core.utils.startSharePlainTextIntent +import im.vector.app.databinding.DialogBaseEditTextBinding import im.vector.app.databinding.DialogShareQrCodeBinding import im.vector.app.databinding.FragmentMatrixProfileBinding import im.vector.app.databinding.ViewStubRoomMemberProfileHeaderBinding @@ -50,9 +51,11 @@ import im.vector.app.features.crypto.verification.VerificationBottomSheet import im.vector.app.features.home.AvatarRenderer import im.vector.app.features.home.room.detail.RoomDetailPendingAction import im.vector.app.features.home.room.detail.RoomDetailPendingActionStore +import im.vector.app.features.home.room.detail.timeline.helper.MatrixItemColorProvider import im.vector.app.features.roommemberprofile.devices.DeviceListBottomSheet import im.vector.app.features.roommemberprofile.powerlevel.EditPowerLevelDialogs import kotlinx.parcelize.Parcelize +import org.matrix.android.sdk.api.session.accountdata.UserAccountDataTypes import org.matrix.android.sdk.api.session.room.powerlevels.Role import org.matrix.android.sdk.api.util.MatrixItem import javax.inject.Inject @@ -67,7 +70,8 @@ class RoomMemberProfileFragment @Inject constructor( val viewModelFactory: RoomMemberProfileViewModel.Factory, private val roomMemberProfileController: RoomMemberProfileController, private val avatarRenderer: AvatarRenderer, - private val roomDetailPendingActionStore: RoomDetailPendingActionStore + private val roomDetailPendingActionStore: RoomDetailPendingActionStore, + private val matrixItemColorProvider: MatrixItemColorProvider ) : VectorBaseFragment(), RoomMemberProfileController.Callback { @@ -199,6 +203,7 @@ class RoomMemberProfileFragment @Inject constructor( headerViews.memberProfileIdView.text = userMatrixItem.id val bestName = userMatrixItem.getBestName() headerViews.memberProfileNameView.text = bestName + headerViews.memberProfileNameView.setTextColor(matrixItemColorProvider.getColor(userMatrixItem)) views.matrixProfileToolbarTitleView.text = bestName avatarRenderer.render(userMatrixItem, headerViews.memberProfileAvatarView) avatarRenderer.render(userMatrixItem, views.matrixProfileToolbarAvatarImageView) @@ -237,6 +242,9 @@ class RoomMemberProfileFragment @Inject constructor( headerViews.memberProfileAvatarView.setOnClickListener { view -> onAvatarClicked(view, userMatrixItem) } + headerViews.memberProfileNameView.setOnClickListener { _ -> + onProfileNameClicked(userMatrixItem) + } views.matrixProfileToolbarAvatarImageView.setOnClickListener { view -> onAvatarClicked(view, userMatrixItem) } @@ -323,6 +331,40 @@ class RoomMemberProfileFragment @Inject constructor( navigator.openBigImageViewer(requireActivity(), view, userMatrixItem) } + private fun onProfileNameClicked(userMatrixItem: MatrixItem) { + val inflater = requireActivity().layoutInflater + val layout = inflater.inflate(R.layout.dialog_base_edit_text, null) + val views = DialogBaseEditTextBinding.bind(layout) + val session = injector().activeSessionHolder().getActiveSession() + val overrideColorsSetting = session.getAccountDataEvent(UserAccountDataTypes.TYPE_OVERRIDE_COLORS) + val overrideColorSpecs = overrideColorsSetting?.content?.toMap().orEmpty() + val overrideColorSpec = overrideColorSpecs[userMatrixItem.id]?.toString() + views.editText.setText(overrideColorSpec) + views.editText.hint = "#000000" + + AlertDialog.Builder(requireActivity()) + .setTitle(R.string.room_member_override_color) + .setView(layout) + .setPositiveButton(R.string.ok) { _, _ -> + val newOverrideColorSpec = views.editText.text.toString() + if (newOverrideColorSpec != overrideColorSpec) { + val newOverrideColorSpecs = overrideColorSpecs.toMutableMap() + if (matrixItemColorProvider.setOverrideColor(userMatrixItem.id, newOverrideColorSpec)) { + newOverrideColorSpecs[userMatrixItem.id] = newOverrideColorSpec + } else { + newOverrideColorSpecs.remove(userMatrixItem.id) + } + session.updateAccountData( + type = UserAccountDataTypes.TYPE_OVERRIDE_COLORS, + content = newOverrideColorSpecs + ) + headerViews.memberProfileNameView.setTextColor(matrixItemColorProvider.getColor(userMatrixItem)) + } + } + .setNegativeButton(R.string.cancel, null) + .show() + } + override fun onEditPowerLevel(currentRole: Role) { EditPowerLevelDialogs.showChoice(requireActivity(), currentRole) { newPowerLevel -> viewModel.handle(RoomMemberProfileAction.SetPowerLevel(currentRole.value, newPowerLevel, true)) diff --git a/vector/src/main/res/values/strings.xml b/vector/src/main/res/values/strings.xml index 76541460d2..61bc5dec94 100644 --- a/vector/src/main/res/values/strings.xml +++ b/vector/src/main/res/values/strings.xml @@ -2241,6 +2241,8 @@ Leave "Leaving the room…" + Override color + Admins Moderators Custom