From 460a72e6b562dbf5b753acc2d64fa7d104ef94e2 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 5 Apr 2019 17:04:25 +0200 Subject: [PATCH] Create Room --- .../android/api/session/events/model/Event.kt | 2 +- .../android/api/session/room/RoomService.kt | 8 + .../room/model/create/CreateRoomParams.kt | 253 ++++++++++++++++++ .../room/model/create/CreateRoomPreset.kt | 30 +++ .../room/model/create/CreateRoomResponse.kt | 25 ++ .../session/room/model/create/Invite3Pid.kt | 42 +++ .../internal/session/DefaultSession.kt | 7 +- .../android/internal/session/SessionModule.kt | 2 +- .../session/room/DefaultRoomService.kt | 16 +- .../android/internal/session/room/RoomAPI.kt | 18 +- .../internal/session/room/RoomModule.kt | 6 + .../session/room/create/CreateRoomTask.kt | 88 ++++++ .../session/room/send/DefaultSendService.kt | 1 + .../features/home/HomeActivity.kt | 23 +- .../features/home/HomeActivityViewModel.kt | 23 ++ vector/src/main/res/menu/home.xml | 5 + 16 files changed, 538 insertions(+), 11 deletions(-) create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/create/CreateRoomParams.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/create/CreateRoomPreset.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/create/CreateRoomResponse.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/create/Invite3Pid.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/create/CreateRoomTask.kt diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/events/model/Event.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/events/model/Event.kt index 9a66ffed62..7c6ca39bcb 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/events/model/Event.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/events/model/Event.kt @@ -54,7 +54,7 @@ inline fun T?.toContent(): Content? { @JsonClass(generateAdapter = true) data class Event( @Json(name = "type") val type: String, - @Json(name = "event_id") val eventId: String?, + @Json(name = "event_id") val eventId: String? = null, @Json(name = "content") val content: Content? = null, @Json(name = "prev_content") val prevContent: Content? = null, @Json(name = "origin_server_ts") val originServerTs: Long? = null, diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/RoomService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/RoomService.kt index 79b514fdc9..fc0bf49955 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/RoomService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/RoomService.kt @@ -17,13 +17,21 @@ package im.vector.matrix.android.api.session.room import androidx.lifecycle.LiveData +import im.vector.matrix.android.api.MatrixCallback import im.vector.matrix.android.api.session.room.model.RoomSummary +import im.vector.matrix.android.api.session.room.model.create.CreateRoomParams /** * This interface defines methods to get rooms. It's implemented at the session level. */ interface RoomService { + /** + * Create a room + */ + fun createRoom(createRoomParams: CreateRoomParams, + callback: MatrixCallback) + /** * Get a room from a roomId * @param roomId the roomId to look for. diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/create/CreateRoomParams.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/create/CreateRoomParams.kt new file mode 100644 index 0000000000..8347d0af46 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/create/CreateRoomParams.kt @@ -0,0 +1,253 @@ +/* + * 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.matrix.android.api.session.room.model.create + +import android.util.Patterns +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass +import im.vector.matrix.android.api.MatrixPatterns.isUserId +import im.vector.matrix.android.api.auth.data.Credentials +import im.vector.matrix.android.api.auth.data.HomeServerConnectionConfig +import im.vector.matrix.android.api.session.events.model.Event +import im.vector.matrix.android.api.session.events.model.EventType +import im.vector.matrix.android.api.session.events.model.toContent +import im.vector.matrix.android.api.session.room.model.PowerLevels +import im.vector.matrix.android.api.session.room.model.RoomDirectoryVisibility +import im.vector.matrix.android.api.session.room.model.RoomHistoryVisibility +import im.vector.matrix.android.internal.auth.data.ThreePidMedium +import java.util.* + +/** + * Parameter to create a room, with facilities functions to configure it + */ +@JsonClass(generateAdapter = true) +class CreateRoomParams { + + /** + * A public visibility indicates that the room will be shown in the published room list. + * A private visibility will hide the room from the published room list. + * Rooms default to private visibility if this key is not included. + * NB: This should not be confused with join_rules which also uses the word public. One of: ["public", "private"] + */ + var visibility: RoomDirectoryVisibility? = null + + /** + * The desired room alias local part. If this is included, a room alias will be created and mapped to the newly created room. + * The alias will belong on the same homeserver which created the room. + * For example, if this was set to "foo" and sent to the homeserver "example.com" the complete room alias would be #foo:example.com. + */ + @Json(name = "room_alias_name") + var roomAliasName: String? = null + + /** + * If this is included, an m.room.name event will be sent into the room to indicate the name of the room. + * See Room Events for more information on m.room.name. + */ + var name: String? = null + + /** + * If this is included, an m.room.topic event will be sent into the room to indicate the topic for the room. + * See Room Events for more information on m.room.topic. + */ + var topic: String? = null + + /** + * A list of user IDs to invite to the room. + * This will tell the server to invite everyone in the list to the newly created room. + */ + @Json(name = "invite") + var invitedUserIds: MutableList? = null + + /** + * A list of objects representing third party IDs to invite into the room. + */ + @Json(name = "invite_3pid") + var invite3pids: MutableList? = null + + /** + * Extra keys to be added to the content of the m.room.create. + * The server will clobber the following keys: creator. + * Future versions of the specification may allow the server to clobber other keys. + */ + @Json(name = "creation_content") + var creationContent: Any? = null + + /** + * A list of state events to set in the new room. + * This allows the user to override the default state events set in the new room. + * The expected format of the state events are an object with type, state_key and content keys set. + * Takes precedence over events set by presets, but gets overriden by name and topic keys. + */ + @Json(name = "initial_state") + var initialStates: MutableList? = null + + /** + * Convenience parameter for setting various default state events based on a preset. Must be either: + * private_chat => join_rules is set to invite. history_visibility is set to shared. + * trusted_private_chat => join_rules is set to invite. history_visibility is set to shared. All invitees are given the same power level as the + * room creator. + * public_chat: => join_rules is set to public. history_visibility is set to shared. One of: ["private_chat", "public_chat", "trusted_private_chat"] + */ + var preset: CreateRoomPreset? = null + + /** + * This flag makes the server set the is_direct flag on the m.room.member events sent to the users in invite and invite_3pid. + * See Direct Messaging for more information. + */ + @Json(name = "is_direct") + var isDirect: Boolean? = null + + /** + * The power level content to override in the default power level event + */ + @Json(name = "power_level_content_override") + var powerLevelContentOverride: PowerLevels? = null + + /** + * Add the crypto algorithm to the room creation parameters. + * + * @param algorithm the algorithm + */ + fun addCryptoAlgorithm(algorithm: String) { + if (algorithm.isNotBlank()) { + val contentMap = HashMap() + contentMap["algorithm"] = algorithm + + val algoEvent = Event(type = EventType.ENCRYPTION, + content = contentMap.toContent() + ) + + if (null == initialStates) { + initialStates = Arrays.asList(algoEvent) + } else { + initialStates!!.add(algoEvent) + } + } + } + + /** + * Force the history visibility in the room creation parameters. + * + * @param historyVisibility the expected history visibility, set null to remove any existing value. + */ + fun setHistoryVisibility(historyVisibility: RoomHistoryVisibility?) { + // Remove the existing value if any. + if (initialStates != null && !initialStates!!.isEmpty()) { + val newInitialStates = ArrayList() + for (event in initialStates!!) { + if (event.type != EventType.STATE_HISTORY_VISIBILITY) { + newInitialStates.add(event) + } + } + initialStates = newInitialStates + } + + if (historyVisibility != null) { + val contentMap = HashMap() + contentMap["history_visibility"] = historyVisibility + + val historyVisibilityEvent = Event(type = EventType.STATE_HISTORY_VISIBILITY, + content = contentMap.toContent()) + + if (null == initialStates) { + initialStates = Arrays.asList(historyVisibilityEvent) + } else { + initialStates!!.add(historyVisibilityEvent) + } + } + } + + /** + * Mark as a direct message room. + */ + fun setDirectMessage() { + preset = CreateRoomPreset.PRESET_TRUSTED_PRIVATE_CHAT + isDirect = true + } + + /** + * @return the invite count + */ + private fun getInviteCount(): Int { + return invitedUserIds?.size ?: 0 + } + + /** + * @return the pid invite count + */ + private fun getInvite3PidCount(): Int { + return invite3pids?.size ?: 0 + } + + /** + * Tells if the created room can be a direct chat one. + * + * @return true if it is a direct chat + */ + fun isDirect(): Boolean { + return preset == CreateRoomPreset.PRESET_TRUSTED_PRIVATE_CHAT + && isDirect == true + && (1 == getInviteCount() || 1 == getInvite3PidCount()) + } + + /** + * @return the first invited user id + */ + fun getFirstInvitedUserId(): String? { + if (0 != getInviteCount()) { + return invitedUserIds!![0] + } + + return if (0 != getInvite3PidCount()) { + invite3pids!![0].address + } else null + } + + /** + * Add some ids to the room creation + * ids might be a matrix id or an email address. + * + * @param ids the participant ids to add. + */ + fun addParticipantIds(hsConfig: HomeServerConnectionConfig, + credentials: Credentials, + ids: List) { + for (id in ids) { + if (Patterns.EMAIL_ADDRESS.matcher(id).matches()) { + if (null == invite3pids) { + invite3pids = ArrayList() + } + + val pid = Invite3Pid(idServer = hsConfig.identityServerUri.host!!, + medium = ThreePidMedium.EMAIL, + address = id) + + invite3pids!!.add(pid) + } else if (isUserId(id)) { + // do not invite oneself + if (credentials.userId != id) { + if (null == invitedUserIds) { + invitedUserIds = ArrayList() + } + + invitedUserIds!!.add(id) + } + } + // TODO add phonenumbers when it will be available + } + } +} \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/create/CreateRoomPreset.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/create/CreateRoomPreset.kt new file mode 100644 index 0000000000..9548062bae --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/create/CreateRoomPreset.kt @@ -0,0 +1,30 @@ +/* + * 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.matrix.android.api.session.room.model.create + +import com.squareup.moshi.Json + +enum class CreateRoomPreset { + @Json(name = "private_chat") + PRESET_PRIVATE_CHAT, + + @Json(name = "public_chat") + PRESET_PUBLIC_CHAT, + + @Json(name = "trusted_private_chat") + PRESET_TRUSTED_PRIVATE_CHAT +} \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/create/CreateRoomResponse.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/create/CreateRoomResponse.kt new file mode 100644 index 0000000000..c437bcfbf1 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/create/CreateRoomResponse.kt @@ -0,0 +1,25 @@ +/* + * 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.matrix.android.api.session.room.model.create + +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass + +@JsonClass(generateAdapter = true) +internal data class CreateRoomResponse( + @Json(name = "room_id") var roomId: String? = null +) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/create/Invite3Pid.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/create/Invite3Pid.kt new file mode 100644 index 0000000000..5ad920a32b --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/create/Invite3Pid.kt @@ -0,0 +1,42 @@ +/* + * 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.matrix.android.api.session.room.model.create + +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass + +@JsonClass(generateAdapter = true) +data class Invite3Pid( + /** + * Required. + * The hostname+port of the identity server which should be used for third party identifier lookups. + */ + @Json(name = "id_server") + val idServer: String, + + /** + * Required. + * The kind of address being passed in the address field, for example email. + */ + val medium: String, + + /** + * Required. + * The invitee's third party identifier. + */ + val address: String +) \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/DefaultSession.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/DefaultSession.kt index 1959cb8ded..a0ccaa9641 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/DefaultSession.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/DefaultSession.kt @@ -30,10 +30,10 @@ import im.vector.matrix.android.api.session.group.model.GroupSummary import im.vector.matrix.android.api.session.room.Room import im.vector.matrix.android.api.session.room.RoomService import im.vector.matrix.android.api.session.room.model.RoomSummary +import im.vector.matrix.android.api.session.room.model.create.CreateRoomParams import im.vector.matrix.android.api.session.signout.SignOutService 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.util.Cancelable import im.vector.matrix.android.internal.database.LiveEntityObserver import im.vector.matrix.android.internal.di.MatrixKoinComponent import im.vector.matrix.android.internal.di.MatrixKoinHolder @@ -131,6 +131,11 @@ internal class DefaultSession(override val sessionParams: SessionParams) : Sessi // ROOM SERVICE + override fun createRoom(createRoomParams: CreateRoomParams, callback: MatrixCallback) { + assert(isOpen) + return roomService.createRoom(createRoomParams, callback) + } + override fun getRoom(roomId: String): Room? { assert(isOpen) return roomService.getRoom(roomId) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/SessionModule.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/SessionModule.kt index bc13d2e7d1..5c18c66adc 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/SessionModule.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/SessionModule.kt @@ -97,7 +97,7 @@ internal class SessionModule(private val sessionParams: SessionParams) { } scope(DefaultSession.SCOPE) { - DefaultRoomService(get(), get()) as RoomService + DefaultRoomService(get(), get(), get(), get()) as RoomService } scope(DefaultSession.SCOPE) { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/DefaultRoomService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/DefaultRoomService.kt index 0b0bae8da7..18de69c4e5 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/DefaultRoomService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/DefaultRoomService.kt @@ -18,18 +18,32 @@ package im.vector.matrix.android.internal.session.room import androidx.lifecycle.LiveData import com.zhuinden.monarchy.Monarchy +import im.vector.matrix.android.api.MatrixCallback import im.vector.matrix.android.api.session.room.Room import im.vector.matrix.android.api.session.room.RoomService import im.vector.matrix.android.api.session.room.model.RoomSummary +import im.vector.matrix.android.api.session.room.model.create.CreateRoomParams import im.vector.matrix.android.internal.database.mapper.asDomain import im.vector.matrix.android.internal.database.model.RoomEntity 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.query.where +import im.vector.matrix.android.internal.session.room.create.CreateRoomTask +import im.vector.matrix.android.internal.task.TaskExecutor +import im.vector.matrix.android.internal.task.configureWith import im.vector.matrix.android.internal.util.fetchManaged internal class DefaultRoomService(private val monarchy: Monarchy, - private val roomFactory: RoomFactory) : RoomService { + private val createRoomTask: CreateRoomTask, + private val roomFactory: RoomFactory, + private val taskExecutor: TaskExecutor) : RoomService { + + override fun createRoom(createRoomParams: CreateRoomParams, callback: MatrixCallback) { + createRoomTask + .configureWith(createRoomParams) + .dispatchTo(callback) + .executeBy(taskExecutor) + } override fun getRoom(roomId: String): Room? { monarchy.fetchManaged { RoomEntity.where(it, roomId).findFirst() } ?: return null diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomAPI.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomAPI.kt index ecb4d42daa..bc44300f4d 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomAPI.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomAPI.kt @@ -18,21 +18,27 @@ package im.vector.matrix.android.internal.session.room import im.vector.matrix.android.api.session.events.model.Content import im.vector.matrix.android.api.session.events.model.Event +import im.vector.matrix.android.api.session.room.model.create.CreateRoomParams +import im.vector.matrix.android.api.session.room.model.create.CreateRoomResponse import im.vector.matrix.android.internal.network.NetworkConstants import im.vector.matrix.android.internal.session.room.members.RoomMembersResponse import im.vector.matrix.android.internal.session.room.send.SendResponse import im.vector.matrix.android.internal.session.room.timeline.EventContextResponse import im.vector.matrix.android.internal.session.room.timeline.PaginationResponse import retrofit2.Call -import retrofit2.http.Body -import retrofit2.http.GET -import retrofit2.http.POST -import retrofit2.http.PUT -import retrofit2.http.Path -import retrofit2.http.Query +import retrofit2.http.* internal interface RoomAPI { + /** + * Create a room. + * Ref: https://matrix.org/docs/spec/client_server/r0.4.0.html#post-matrix-client-r0-createroom + * + * @param param the creation room parameter + */ + @POST(NetworkConstants.URI_API_PREFIX_PATH_R0 + "createRoom") + fun createRoom(@Body param: CreateRoomParams): Call + /** * Get a list of messages starting from a reference. * diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomModule.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomModule.kt index c506cb0da1..34515d30da 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomModule.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomModule.kt @@ -17,6 +17,8 @@ package im.vector.matrix.android.internal.session.room import im.vector.matrix.android.internal.session.DefaultSession +import im.vector.matrix.android.internal.session.room.create.CreateRoomTask +import im.vector.matrix.android.internal.session.room.create.DefaultCreateRoomTask import im.vector.matrix.android.internal.session.room.members.DefaultLoadRoomMembersTask import im.vector.matrix.android.internal.session.room.members.LoadRoomMembersTask import im.vector.matrix.android.internal.session.room.read.DefaultSetReadMarkersTask @@ -64,5 +66,9 @@ class RoomModule { RoomFactory(get(), get(), get(), get(), get(), get(), get(), get()) } + scope(DefaultSession.SCOPE) { + DefaultCreateRoomTask(get(), get()) as CreateRoomTask + } + } } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/create/CreateRoomTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/create/CreateRoomTask.kt new file mode 100644 index 0000000000..b3aa137a03 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/create/CreateRoomTask.kt @@ -0,0 +1,88 @@ +/* + * 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.matrix.android.internal.session.room.create + +import android.os.Handler +import android.os.HandlerThread +import arrow.core.Try +import im.vector.matrix.android.api.session.room.model.create.CreateRoomParams +import im.vector.matrix.android.api.session.room.model.create.CreateRoomResponse +import im.vector.matrix.android.internal.database.model.RoomEntity +import im.vector.matrix.android.internal.database.model.RoomEntityFields +import im.vector.matrix.android.internal.network.executeRequest +import im.vector.matrix.android.internal.session.room.RoomAPI +import im.vector.matrix.android.internal.task.Task +import io.realm.Realm +import io.realm.RealmChangeListener +import io.realm.RealmConfiguration +import io.realm.RealmResults +import java.util.concurrent.CountDownLatch + +private const val THREAD_NAME = "CREATE_ROOM_" + +internal interface CreateRoomTask : Task + + +internal class DefaultCreateRoomTask(private val roomAPI: RoomAPI, + private val realmConfiguration: RealmConfiguration) : CreateRoomTask { + + + override fun execute(params: CreateRoomParams): Try { + return executeRequest { + apiCall = roomAPI.createRoom(params) + }.flatMap { createRoomResponse -> + val roomId = createRoomResponse.roomId!! + + val latch = CountDownLatch(1) + + // Wait for room to come back from the sync (but it can maybe be in the DB is the sync response is received before) + val handlerThread = HandlerThread(THREAD_NAME + hashCode()) + handlerThread.start() + val handler = Handler(handlerThread.looper) + + handler.post { + val realm = Realm.getInstance(realmConfiguration) + + if (realm.where(RoomEntity::class.java) + .equalTo(RoomEntityFields.ROOM_ID, roomId) + .findAll() + .isEmpty()) { + val result = realm.where(RoomEntity::class.java) + .equalTo(RoomEntityFields.ROOM_ID, roomId) + .findAllAsync() + + result.addChangeListener(object : RealmChangeListener> { + override fun onChange(t: RealmResults) { + if (t.isNotEmpty()) { + result.removeChangeListener(this) + realm.close() + latch.countDown() + } + } + }) + } else { + realm.close() + latch.countDown() + } + } + + latch.await() + handlerThread.quit() + return Try.just(roomId) + } + } +} diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/DefaultSendService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/DefaultSendService.kt index ef8569e661..5480cced5f 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/DefaultSendService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/DefaultSendService.kt @@ -41,6 +41,7 @@ internal class DefaultSendService(private val roomId: String, .setRequiredNetworkType(NetworkType.CONNECTED) .build() + // TODO callback is not used override fun sendTextMessage(text: String, callback: MatrixCallback): Cancelable { val event = eventFactory.createTextEvent(roomId, text) diff --git a/vector/src/main/java/im/vector/riotredesign/features/home/HomeActivity.kt b/vector/src/main/java/im/vector/riotredesign/features/home/HomeActivity.kt index de484aa7b1..e3f443e5a9 100644 --- a/vector/src/main/java/im/vector/riotredesign/features/home/HomeActivity.kt +++ b/vector/src/main/java/im/vector/riotredesign/features/home/HomeActivity.kt @@ -16,6 +16,7 @@ package im.vector.riotredesign.features.home +import android.app.ProgressDialog import android.content.Context import android.content.Intent import android.os.Bundle @@ -26,6 +27,7 @@ import androidx.appcompat.widget.Toolbar import androidx.core.view.GravityCompat import androidx.drawerlayout.widget.DrawerLayout import androidx.fragment.app.FragmentManager +import androidx.lifecycle.Observer import com.airbnb.mvrx.viewModel import im.vector.matrix.android.api.Matrix import im.vector.riotredesign.R @@ -51,15 +53,18 @@ class HomeActivity : VectorBaseActivity(), ToolbarConfigurable { private val homeActivityViewModel: HomeActivityViewModel by viewModel() private val homeNavigator by inject() + private var progress: ProgressDialog? = null + private val drawerListener = object : DrawerLayout.SimpleDrawerListener() { override fun onDrawerStateChanged(newState: Int) { hideKeyboard() } } + override fun getLayoutRes() = R.layout.activity_home + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - setContentView(R.layout.activity_home) bindScope(getOrCreateScope(HomeModule.HOME_SCOPE)) homeNavigator.activity = this drawerLayout.addDrawerListener(drawerListener) @@ -72,6 +77,17 @@ class HomeActivity : VectorBaseActivity(), ToolbarConfigurable { homeActivityViewModel.openRoomLiveData.observeEvent(this) { homeNavigator.openRoomDetail(it, null) } + homeActivityViewModel.isLoading.observe(this, Observer { + // TODO better UI + if (it) { + progress?.dismiss() + progress = ProgressDialog(this) + progress?.setMessage(getString(R.string.room_recents_create_room)) + progress?.show() + } else { + progress?.dismiss() + } + }) } override fun onDestroy() { @@ -120,6 +136,11 @@ class HomeActivity : VectorBaseActivity(), ToolbarConfigurable { SignOutUiWorker(this).perform(Matrix.getInstance().currentSession!!) return true } + // TODO Temporary code here to create a room + R.id.tmp_menu_create_room -> { + homeActivityViewModel.createRoom() + return true + } } return true diff --git a/vector/src/main/java/im/vector/riotredesign/features/home/HomeActivityViewModel.kt b/vector/src/main/java/im/vector/riotredesign/features/home/HomeActivityViewModel.kt index cb883a79ce..c40a488f33 100644 --- a/vector/src/main/java/im/vector/riotredesign/features/home/HomeActivityViewModel.kt +++ b/vector/src/main/java/im/vector/riotredesign/features/home/HomeActivityViewModel.kt @@ -22,7 +22,9 @@ import com.airbnb.mvrx.MvRxState import com.airbnb.mvrx.MvRxViewModelFactory import com.airbnb.mvrx.ViewModelContext import im.vector.matrix.android.api.Matrix +import im.vector.matrix.android.api.MatrixCallback import im.vector.matrix.android.api.session.Session +import im.vector.matrix.android.api.session.room.model.create.CreateRoomParams import im.vector.matrix.rx.rx import im.vector.riotredesign.core.platform.VectorViewModel import im.vector.riotredesign.core.utils.LiveEvent @@ -47,6 +49,10 @@ class HomeActivityViewModel(state: EmptyState, } } + private val _isLoading = MutableLiveData() + val isLoading: LiveData + get() = _isLoading + private val _openRoomLiveData = MutableLiveData>() val openRoomLiveData: LiveData> get() = _openRoomLiveData @@ -73,5 +79,22 @@ class HomeActivityViewModel(state: EmptyState, .disposeOnClear() } + fun createRoom(createRoomParams: CreateRoomParams = CreateRoomParams()) { + _isLoading.value = true + + session.createRoom(createRoomParams, object : MatrixCallback { + override fun onSuccess(data: String) { + _isLoading.value = false + // Open room id + _openRoomLiveData.postValue(LiveEvent(data)) + } + + override fun onFailure(failure: Throwable) { + _isLoading.value = false + super.onFailure(failure) + } + }) + } + } \ No newline at end of file diff --git a/vector/src/main/res/menu/home.xml b/vector/src/main/res/menu/home.xml index 74cf01691a..2c8e3c0b30 100644 --- a/vector/src/main/res/menu/home.xml +++ b/vector/src/main/res/menu/home.xml @@ -11,4 +11,9 @@ android:icon="@drawable/ic_material_exit_to_app" android:title="@string/action_sign_out" /> + + + \ No newline at end of file