Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Room Settings: Name, Topic, Photo, Aliases, History Visibility #1464

Merged
merged 29 commits into from
Jun 30, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
05e8482
Room name and topic fields added to form.
onurays Jun 9, 2020
bfebaa5
Show/hide save action button dynamically.
onurays Jun 10, 2020
a6e4a32
Chain all operations to save settings.
onurays Jun 10, 2020
52eec06
Updating room avatar is implemented.
onurays Jun 12, 2020
e1a12f4
Show current history readability.
onurays Jun 14, 2020
f5790e5
Implementation of room history readability.
onurays Jun 22, 2020
762dd1d
Implementation of canonical alias.
onurays Jun 23, 2020
1f30cf4
Check if user have enough power level to change settings.
onurays Jun 23, 2020
7f2ce91
Add camera and gallery options to set room avatar.
onurays Jun 24, 2020
ef1ae41
Change room avatar in BigImageViewer.
onurays Jun 24, 2020
8787f5d
Remove room avatar item from room settings.
onurays Jun 24, 2020
a03f69f
Check if user has enough power level to change room avatar.
onurays Jun 24, 2020
16bd642
Implementation of updating user avatar.
onurays Jun 25, 2020
e0e4cf3
Code review fixes.
onurays Jun 29, 2020
a93cbf3
Lint fix.
onurays Jun 29, 2020
56f8e52
Simplify uploading room and user avatar.
onurays Jun 29, 2020
512e4f0
Create UCropHelper with default settings.
onurays Jun 29, 2020
ad084e1
Use simple dialog for avatar selection.
onurays Jun 29, 2020
32721ca
Code review fix.
onurays Jun 29, 2020
4d6ba5a
Check permission before triggering history readability click action.
onurays Jun 29, 2020
2650453
Add room alias first before setting the canonical alias.
onurays Jun 29, 2020
5f788d9
Use AlertDialog from v7 compat lib.
onurays Jun 29, 2020
b3d4d20
Check permission before trying to reach Camera.
onurays Jun 30, 2020
e0ea0c1
Hide save action after saving completed.
onurays Jun 30, 2020
cca6d0e
Cleanup
bmarty Jun 30, 2020
ff0b922
Create RoomHistoryVisibilityFormatter
bmarty Jun 30, 2020
da472ea
Use name instead of computed displayName
bmarty Jun 30, 2020
e658ef1
Fix issue with save action visibility
bmarty Jun 30, 2020
962e11a
Onuray's remark :)
bmarty Jun 30, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ Improvements 🙌:
- "Add Matrix app" menu is now always visible (#1495)
- Handle `/op`, `/deop`, and `/nick` commands (#12)
- Prioritising Recovery key over Recovery passphrase (#1463)
- Room Settings: Name, Topic, Photo, Aliases, History Visibility (#1455)
- Update user avatar (#1054)

Bugfix 🐛:
- Fix dark theme issue on login screen (#1097)
Expand Down
26 changes: 26 additions & 0 deletions matrix-sdk-android-rx/src/main/java/im/vector/matrix/rx/RxRoom.kt
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,14 @@

package im.vector.matrix.rx

import android.net.Uri
import im.vector.matrix.android.api.query.QueryStringValue
import im.vector.matrix.android.api.session.events.model.Event
import im.vector.matrix.android.api.session.room.Room
import im.vector.matrix.android.api.session.room.members.RoomMemberQueryParams
import im.vector.matrix.android.api.session.room.model.EventAnnotationsSummary
import im.vector.matrix.android.api.session.room.model.ReadReceipt
import im.vector.matrix.android.api.session.room.model.RoomHistoryVisibility
import im.vector.matrix.android.api.session.room.model.RoomMemberSummary
import im.vector.matrix.android.api.session.room.model.RoomSummary
import im.vector.matrix.android.api.session.room.notification.RoomNotificationState
Expand Down Expand Up @@ -101,6 +103,30 @@ class RxRoom(private val room: Room) {
fun invite(userId: String, reason: String? = null): Completable = completableBuilder<Unit> {
room.invite(userId, reason, it)
}

fun updateTopic(topic: String): Completable = completableBuilder<Unit> {
room.updateTopic(topic, it)
}

fun updateName(name: String): Completable = completableBuilder<Unit> {
room.updateName(name, it)
}

fun addRoomAlias(alias: String): Completable = completableBuilder<Unit> {
room.addRoomAlias(alias, it)
}

fun updateCanonicalAlias(alias: String): Completable = completableBuilder<Unit> {
room.updateCanonicalAlias(alias, it)
}

fun updateHistoryReadability(readability: RoomHistoryVisibility): Completable = completableBuilder<Unit> {
room.updateHistoryReadability(readability, it)
}

fun updateAvatar(avatarUri: Uri, fileName: String): Completable = completableBuilder<Unit> {
room.updateAvatar(avatarUri, fileName, it)
}
}

fun Room.rx(): RxRoom {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

package im.vector.matrix.android.api.session.profile

import android.net.Uri
import androidx.lifecycle.LiveData
import im.vector.matrix.android.api.MatrixCallback
import im.vector.matrix.android.api.session.identity.ThreePid
Expand Down Expand Up @@ -48,6 +49,14 @@ interface ProfileService {
*/
fun setDisplayName(userId: String, newDisplayName: String, matrixCallback: MatrixCallback<Unit>): Cancelable

/**
* Update the avatar for this user
* @param userId the userId to update the avatar of
* @param newAvatarUri the new avatar uri of the user
* @param fileName the fileName of selected image
*/
fun updateAvatar(userId: String, newAvatarUri: Uri, fileName: String, matrixCallback: MatrixCallback<Unit>): Cancelable

/**
* Return the current avatarUrl for this user.
* @param userId the userId param to look for
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@ import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
*/
data class RoomSummary constructor(
val roomId: String,
// Computed display name
val displayName: String = "",
val name: String = "",
val topic: String = "",
val avatarUrl: String = "",
val canonicalAlias: String? = null,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

package im.vector.matrix.android.api.session.room.powerlevels

import im.vector.matrix.android.api.session.events.model.EventType
import im.vector.matrix.android.api.session.room.model.PowerLevelsContent

/**
Expand Down Expand Up @@ -123,4 +124,59 @@ class PowerLevelsHelper(private val powerLevelsContent: PowerLevelsContent) {
else -> Role.Moderator.value
}
}

/**
* Check if user have the necessary power level to change room name
* @param userId the id of the user to check for.
* @return true if able to change room name
*/
fun isUserAbleToChangeRoomName(userId: String): Boolean {
val powerLevel = getUserPowerLevelValue(userId)
val minPowerLevel = powerLevelsContent.events[EventType.STATE_ROOM_NAME] ?: powerLevelsContent.stateDefault
return powerLevel >= minPowerLevel
}

/**
* Check if user have the necessary power level to change room topic
* @param userId the id of the user to check for.
* @return true if able to change room topic
*/
fun isUserAbleToChangeRoomTopic(userId: String): Boolean {
val powerLevel = getUserPowerLevelValue(userId)
val minPowerLevel = powerLevelsContent.events[EventType.STATE_ROOM_TOPIC] ?: powerLevelsContent.stateDefault
return powerLevel >= minPowerLevel
}

/**
* Check if user have the necessary power level to change room canonical alias
* @param userId the id of the user to check for.
* @return true if able to change room canonical alias
*/
fun isUserAbleToChangeRoomCanonicalAlias(userId: String): Boolean {
val powerLevel = getUserPowerLevelValue(userId)
val minPowerLevel = powerLevelsContent.events[EventType.STATE_ROOM_CANONICAL_ALIAS] ?: powerLevelsContent.stateDefault
return powerLevel >= minPowerLevel
}

/**
* Check if user have the necessary power level to change room history readability
* @param userId the id of the user to check for.
* @return true if able to change room history readability
*/
fun isUserAbleToChangeRoomHistoryReadability(userId: String): Boolean {
val powerLevel = getUserPowerLevelValue(userId)
val minPowerLevel = powerLevelsContent.events[EventType.STATE_ROOM_HISTORY_VISIBILITY] ?: powerLevelsContent.stateDefault
return powerLevel >= minPowerLevel
}

/**
* Check if user have the necessary power level to change room avatar
* @param userId the id of the user to check for.
* @return true if able to change room avatar
*/
fun isUserAbleToChangeRoomAvatar(userId: String): Boolean {
val powerLevel = getUserPowerLevelValue(userId)
val minPowerLevel = powerLevelsContent.events[EventType.STATE_ROOM_AVATAR] ?: powerLevelsContent.stateDefault
return powerLevel >= minPowerLevel
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,12 @@

package im.vector.matrix.android.api.session.room.state

import android.net.Uri
import androidx.lifecycle.LiveData
import im.vector.matrix.android.api.MatrixCallback
import im.vector.matrix.android.api.query.QueryStringValue
import im.vector.matrix.android.api.session.events.model.Event
import im.vector.matrix.android.api.session.room.model.RoomHistoryVisibility
import im.vector.matrix.android.api.util.Cancelable
import im.vector.matrix.android.api.util.JsonDict
import im.vector.matrix.android.api.util.Optional
Expand All @@ -31,6 +33,31 @@ interface StateService {
*/
fun updateTopic(topic: String, callback: MatrixCallback<Unit>): Cancelable

/**
* Update the name of the room
*/
fun updateName(name: String, callback: MatrixCallback<Unit>): Cancelable

/**
* Add new alias to the room.
*/
fun addRoomAlias(roomAlias: String, callback: MatrixCallback<Unit>): Cancelable

/**
* Update the canonical alias of the room
*/
fun updateCanonicalAlias(alias: String, callback: MatrixCallback<Unit>): Cancelable

/**
* Update the history readability of the room
*/
fun updateHistoryReadability(readability: RoomHistoryVisibility, callback: MatrixCallback<Unit>): Cancelable

/**
* Update the avatar of the room
*/
fun updateAvatar(avatarUri: Uri, fileName: String, callback: MatrixCallback<Unit>): Cancelable

fun sendStateEvent(eventType: String, stateKey: String?, body: JsonDict, callback: MatrixCallback<Unit>): Cancelable

fun getStateEvent(eventType: String, stateKey: QueryStringValue = QueryStringValue.NoCondition): Event?
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ internal class RoomSummaryMapper @Inject constructor(private val timelineEventMa
return RoomSummary(
roomId = roomSummaryEntity.roomId,
displayName = roomSummaryEntity.displayName ?: "",
name = roomSummaryEntity.name ?: "",
topic = roomSummaryEntity.topic ?: "",
avatarUrl = roomSummaryEntity.avatarUrl ?: "",
isDirect = roomSummaryEntity.isDirect,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ internal open class RoomSummaryEntity(
@PrimaryKey var roomId: String = "",
var displayName: String? = "",
var avatarUrl: String? = "",
var name: String? = "",
var topic: String? = "",
var latestPreviewableEvent: TimelineEventEntity? = null,
var heroes: RealmList<String> = RealmList(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,16 @@

package im.vector.matrix.android.internal.session.content

import android.content.Context
import android.net.Uri
import com.squareup.moshi.Moshi
import im.vector.matrix.android.api.session.content.ContentUrlResolver
import im.vector.matrix.android.internal.di.Authenticated
import im.vector.matrix.android.internal.network.ProgressRequestBody
import im.vector.matrix.android.internal.network.awaitResponse
import im.vector.matrix.android.internal.network.toFailure
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.OkHttpClient
Expand All @@ -31,12 +35,14 @@ import okhttp3.RequestBody.Companion.asRequestBody
import okhttp3.RequestBody.Companion.toRequestBody
import org.greenrobot.eventbus.EventBus
import java.io.File
import java.io.FileNotFoundException
import java.io.IOException
import javax.inject.Inject

internal class FileUploader @Inject constructor(@Authenticated
private val okHttpClient: OkHttpClient,
private val eventBus: EventBus,
private val context: Context,
contentUrlResolver: ContentUrlResolver,
moshi: Moshi) {

Expand All @@ -59,6 +65,19 @@ internal class FileUploader @Inject constructor(@Authenticated
return upload(uploadBody, filename, progressListener)
}

suspend fun uploadFromUri(uri: Uri,
filename: String?,
mimeType: String?,
progressListener: ProgressRequestBody.Listener? = null): ContentUploadResponse {
val inputStream = withContext(Dispatchers.IO) {
context.contentResolver.openInputStream(uri)
} ?: throw FileNotFoundException()

inputStream.use {
return uploadByteArray(it.readBytes(), filename, mimeType, progressListener)
}
}

private suspend fun upload(uploadBody: RequestBody, filename: String?, progressListener: ProgressRequestBody.Listener?): ContentUploadResponse {
val urlBuilder = uploadUrl.toHttpUrlOrNull()?.newBuilder() ?: throw RuntimeException()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

package im.vector.matrix.android.internal.session.profile

import android.net.Uri
import androidx.lifecycle.LiveData
import com.zhuinden.monarchy.Monarchy
import im.vector.matrix.android.api.MatrixCallback
Expand All @@ -27,16 +28,22 @@ import im.vector.matrix.android.api.util.JsonDict
import im.vector.matrix.android.api.util.Optional
import im.vector.matrix.android.internal.database.model.UserThreePidEntity
import im.vector.matrix.android.internal.di.SessionDatabase
import im.vector.matrix.android.internal.session.content.FileUploader
import im.vector.matrix.android.internal.task.TaskExecutor
import im.vector.matrix.android.internal.task.configureWith
import im.vector.matrix.android.internal.task.launchToCallback
import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers
import io.realm.kotlin.where
import javax.inject.Inject

internal class DefaultProfileService @Inject constructor(private val taskExecutor: TaskExecutor,
@SessionDatabase private val monarchy: Monarchy,
private val coroutineDispatchers: MatrixCoroutineDispatchers,
private val refreshUserThreePidsTask: RefreshUserThreePidsTask,
private val getProfileInfoTask: GetProfileInfoTask,
private val setDisplayNameTask: SetDisplayNameTask) : ProfileService {
private val setDisplayNameTask: SetDisplayNameTask,
private val setAvatarUrlTask: SetAvatarUrlTask,
private val fileUploader: FileUploader) : ProfileService {

override fun getDisplayName(userId: String, matrixCallback: MatrixCallback<Optional<String>>): Cancelable {
val params = GetProfileInfoTask.Params(userId)
Expand Down Expand Up @@ -64,6 +71,17 @@ internal class DefaultProfileService @Inject constructor(private val taskExecuto
.executeBy(taskExecutor)
}

override fun updateAvatar(userId: String, newAvatarUri: Uri, fileName: String, matrixCallback: MatrixCallback<Unit>): Cancelable {
return taskExecutor.executorScope.launchToCallback(coroutineDispatchers.main, matrixCallback) {
val response = fileUploader.uploadFromUri(newAvatarUri, fileName, "image/jpeg")
setAvatarUrlTask
.configureWith(SetAvatarUrlTask.Params(userId = userId, newAvatarUrl = response.contentUri)) {
callback = matrixCallback
}
.executeBy(taskExecutor)
}
}

override fun getAvatarUrl(userId: String, matrixCallback: MatrixCallback<Optional<String>>): Cancelable {
val params = GetProfileInfoTask.Params(userId)
return getProfileInfoTask
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,12 @@ internal interface ProfileAPI {
@PUT(NetworkConstants.URI_API_PREFIX_PATH_R0 + "profile/{userId}/displayname")
fun setDisplayName(@Path("userId") userId: String, @Body body: SetDisplayNameBody): Call<Unit>

/**
* Change user avatar url.
*/
@PUT(NetworkConstants.URI_API_PREFIX_PATH_R0 + "profile/{userId}/avatar_url")
fun setAvatarUrl(@Path("userId") userId: String, @Body body: SetAvatarUrlBody): Call<Unit>

/**
* Bind a threePid
* Ref: https://matrix.org/docs/spec/client_server/latest#post-matrix-client-r0-account-3pid-bind
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,4 +54,7 @@ internal abstract class ProfileModule {

@Binds
abstract fun bindSetDisplayNameTask(task: DefaultSetDisplayNameTask): SetDisplayNameTask

@Binds
abstract fun bindSetAvatarUrlTask(task: DefaultSetAvatarUrlTask): SetAvatarUrlTask
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*
* 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.matrix.android.internal.session.profile

import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass

@JsonClass(generateAdapter = true)
internal data class SetAvatarUrlBody(
/**
* The new avatar url for this user.
*/
@Json(name = "avatar_url")
val avatarUrl: String
)
Loading