mirror of
https://github.com/element-hq/element-android
synced 2024-11-23 18:05:36 +03:00
Get Public rooms and join public room
This commit is contained in:
parent
5da29e8063
commit
877de1f597
33 changed files with 1324 additions and 18 deletions
|
@ -45,10 +45,10 @@ android {
|
|||
|
||||
debug {
|
||||
// Set to true to log privacy or sensible data, such as token
|
||||
buildConfigField "boolean", "LOG_PRIVATE_DATA", "false"
|
||||
buildConfigField "boolean", "LOG_PRIVATE_DATA", "true"
|
||||
|
||||
// Set to BODY instead of NONE to enable logging
|
||||
buildConfigField "okhttp3.logging.HttpLoggingInterceptor.Level", "OKHTTP_LOGGING_LEVEL", "okhttp3.logging.HttpLoggingInterceptor.Level.NONE"
|
||||
buildConfigField "okhttp3.logging.HttpLoggingInterceptor.Level", "OKHTTP_LOGGING_LEVEL", "okhttp3.logging.HttpLoggingInterceptor.Level.BODY"
|
||||
}
|
||||
|
||||
release {
|
||||
|
|
|
@ -23,6 +23,7 @@ import im.vector.matrix.android.api.session.content.ContentUploadStateTracker
|
|||
import im.vector.matrix.android.api.session.content.ContentUrlResolver
|
||||
import im.vector.matrix.android.api.session.crypto.CryptoService
|
||||
import im.vector.matrix.android.api.session.group.GroupService
|
||||
import im.vector.matrix.android.api.session.room.RoomDirectoryService
|
||||
import im.vector.matrix.android.api.session.room.RoomService
|
||||
import im.vector.matrix.android.api.session.signout.SignOutService
|
||||
import im.vector.matrix.android.api.session.sync.FilterService
|
||||
|
@ -34,6 +35,7 @@ import im.vector.matrix.android.api.session.user.UserService
|
|||
*/
|
||||
interface Session :
|
||||
RoomService,
|
||||
RoomDirectoryService,
|
||||
GroupService,
|
||||
UserService,
|
||||
CryptoService,
|
||||
|
|
|
@ -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
|
||||
|
||||
import im.vector.matrix.android.api.MatrixCallback
|
||||
import im.vector.matrix.android.api.session.room.model.roomdirectory.PublicRoomsParams
|
||||
import im.vector.matrix.android.api.session.room.model.roomdirectory.PublicRoomsResponse
|
||||
import im.vector.matrix.android.api.util.Cancelable
|
||||
|
||||
/**
|
||||
* This interface defines methods to get and join public rooms. It's implemented at the session level.
|
||||
*/
|
||||
interface RoomDirectoryService {
|
||||
|
||||
/**
|
||||
* Get rooms from directory
|
||||
*/
|
||||
fun getPublicRooms(server: String?,
|
||||
publicRoomsParams: PublicRoomsParams,
|
||||
callback: MatrixCallback<PublicRoomsResponse>): Cancelable
|
||||
|
||||
/**
|
||||
* Join a room by id
|
||||
*/
|
||||
fun joinRoom(roomId: String,
|
||||
callback: MatrixCallback<Unit>)
|
||||
|
||||
}
|
|
@ -0,0 +1,87 @@
|
|||
/*
|
||||
* Copyright 2014 OpenMarket 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.roomdirectory
|
||||
|
||||
import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.JsonClass
|
||||
|
||||
/**
|
||||
* Class representing the objects returned by /publicRooms call.
|
||||
*/
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class PublicRoom(
|
||||
|
||||
/**
|
||||
* Aliases of the room. May be empty.
|
||||
*/
|
||||
@Json(name = "aliases")
|
||||
var aliases: List<String>? = null,
|
||||
|
||||
/**
|
||||
* The canonical alias of the room, if any.
|
||||
*/
|
||||
@Json(name = "canonical_alias")
|
||||
var canonicalAlias: String? = null,
|
||||
|
||||
/**
|
||||
* The name of the room, if any.
|
||||
*/
|
||||
@Json(name = "name")
|
||||
var name: String? = null,
|
||||
|
||||
/**
|
||||
* Required. The number of members joined to the room.
|
||||
*/
|
||||
@Json(name = "num_joined_members")
|
||||
var numJoinedMembers: Int = 0,
|
||||
|
||||
/**
|
||||
* Required. The ID of the room.
|
||||
*/
|
||||
@Json(name = "room_id")
|
||||
var roomId: String,
|
||||
|
||||
/**
|
||||
* The topic of the room, if any.
|
||||
*/
|
||||
@Json(name = "topic")
|
||||
var topic: String? = null,
|
||||
|
||||
/**
|
||||
* Required. Whether the room may be viewed by guest users without joining.
|
||||
*/
|
||||
@Json(name = "world_readable")
|
||||
var worldReadable: Boolean = false,
|
||||
|
||||
/**
|
||||
* Required. Whether guest users may join the room and participate in it. If they can, they will be subject to ordinary power level rules like any other user.
|
||||
*/
|
||||
@Json(name = "guest_can_join")
|
||||
var guestCanJoin: Boolean = false,
|
||||
|
||||
/**
|
||||
* The URL for the room's avatar, if one is set.
|
||||
*/
|
||||
@Json(name = "avatar_url")
|
||||
var avatarUrl: String? = null,
|
||||
|
||||
/**
|
||||
* Undocumented item
|
||||
*/
|
||||
@Json(name = "m.federate")
|
||||
var isFederated: Boolean = false
|
||||
|
||||
)
|
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* Copyright 2014 OpenMarket 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.roomdirectory
|
||||
|
||||
import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.JsonClass
|
||||
|
||||
/**
|
||||
* Class to define a filter to retrieve public rooms
|
||||
*/
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class PublicRoomsFilter(
|
||||
/**
|
||||
* A string to search for in the room metadata, e.g. name, topic, canonical alias etc. (Optional).
|
||||
*/
|
||||
@Json(name = "generic_search_term")
|
||||
var searchTerm: String? = null
|
||||
)
|
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
* Copyright 2016 OpenMarket Ltd
|
||||
* Copyright 2017 Vector Creations 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.roomdirectory
|
||||
|
||||
import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.JsonClass
|
||||
|
||||
/**
|
||||
* Class to pass parameters to get the public rooms list
|
||||
*/
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class PublicRoomsParams(
|
||||
/**
|
||||
* Limit the number of results returned.
|
||||
*/
|
||||
@Json(name = "limit")
|
||||
var limit: Int? = null,
|
||||
|
||||
/**
|
||||
* A pagination token from a previous request, allowing clients to get the next (or previous) batch of rooms.
|
||||
* The direction of pagination is specified solely by which token is supplied, rather than via an explicit flag.
|
||||
*/
|
||||
@Json(name = "since")
|
||||
var since: String? = null,
|
||||
|
||||
/**
|
||||
* Filter to apply to the results.
|
||||
*/
|
||||
@Json(name = "filter")
|
||||
var filter: PublicRoomsFilter? = null,
|
||||
|
||||
/**
|
||||
* Whether or not to include all known networks/protocols from application services on the homeserver. Defaults to false.
|
||||
*/
|
||||
@Json(name = "includeAllNetworks")
|
||||
var includeAllNetworks: Boolean = false,
|
||||
|
||||
/**
|
||||
* The specific third party network/protocol to request from the homeserver. Can only be used if include_all_networks is false.
|
||||
*/
|
||||
@Json(name = "thirdPartyInstanceId")
|
||||
var thirdPartyInstanceId: String? = null
|
||||
)
|
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* Copyright 2014 OpenMarket 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.roomdirectory
|
||||
|
||||
import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.JsonClass
|
||||
|
||||
/**
|
||||
* Class representing the public rooms request response
|
||||
*/
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class PublicRoomsResponse(
|
||||
/**
|
||||
* A pagination token for the response. The absence of this token means there are no more results to fetch and the client should stop paginating.
|
||||
*/
|
||||
@Json(name = "next_batch")
|
||||
var nextBatch: String? = null,
|
||||
|
||||
/**
|
||||
* A pagination token that allows fetching previous results. The absence of this token means there are no results before this batch,
|
||||
* i.e. this is the first batch.
|
||||
*/
|
||||
@Json(name = "prev_batch")
|
||||
var prevBatch: String? = null,
|
||||
|
||||
/**
|
||||
* A paginated chunk of public rooms.
|
||||
*/
|
||||
@Json(name = "chunk")
|
||||
var chunk: List<PublicRoom>? = null,
|
||||
|
||||
/**
|
||||
* An estimate on the total number of public rooms, if the server has an estimate.
|
||||
*/
|
||||
@Json(name = "total_room_count_estimate")
|
||||
var totalRoomCountEstimate: Int? = null
|
||||
)
|
|
@ -30,13 +30,17 @@ import im.vector.matrix.android.api.session.group.Group
|
|||
import im.vector.matrix.android.api.session.group.GroupService
|
||||
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.RoomDirectoryService
|
||||
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.room.model.roomdirectory.PublicRoomsParams
|
||||
import im.vector.matrix.android.api.session.room.model.roomdirectory.PublicRoomsResponse
|
||||
import im.vector.matrix.android.api.session.signout.SignOutService
|
||||
import im.vector.matrix.android.api.session.sync.FilterService
|
||||
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.api.util.MatrixCallbackDelegate
|
||||
import im.vector.matrix.android.internal.database.LiveEntityObserver
|
||||
import im.vector.matrix.android.internal.di.MatrixKoinComponent
|
||||
|
@ -64,6 +68,7 @@ internal class DefaultSession(override val sessionParams: SessionParams) : Sessi
|
|||
private val liveEntityUpdaters by inject<List<LiveEntityObserver>>()
|
||||
private val sessionListeners by inject<SessionListeners>()
|
||||
private val roomService by inject<RoomService>()
|
||||
private val roomDirectoryService by inject<RoomDirectoryService>()
|
||||
private val groupService by inject<GroupService>()
|
||||
private val userService by inject<UserService>()
|
||||
private val filterService by inject<FilterService>()
|
||||
|
@ -171,6 +176,18 @@ internal class DefaultSession(override val sessionParams: SessionParams) : Sessi
|
|||
return roomService.liveRoomSummaries()
|
||||
}
|
||||
|
||||
// ROOM DIRECTORY SERVICE
|
||||
|
||||
override fun getPublicRooms(server: String?, publicRoomsParams: PublicRoomsParams, callback: MatrixCallback<PublicRoomsResponse>): Cancelable {
|
||||
assert(isOpen)
|
||||
return roomDirectoryService.getPublicRooms(server, publicRoomsParams, callback)
|
||||
}
|
||||
|
||||
override fun joinRoom(roomId: String, callback: MatrixCallback<Unit>) {
|
||||
assert(isOpen)
|
||||
return roomDirectoryService.joinRoom(roomId, callback)
|
||||
}
|
||||
|
||||
// GROUP SERVICE
|
||||
|
||||
override fun getGroup(groupId: String): Group? {
|
||||
|
|
|
@ -21,6 +21,7 @@ import com.zhuinden.monarchy.Monarchy
|
|||
import im.vector.matrix.android.api.auth.data.SessionParams
|
||||
import im.vector.matrix.android.api.session.cache.CacheService
|
||||
import im.vector.matrix.android.api.session.group.GroupService
|
||||
import im.vector.matrix.android.api.session.room.RoomDirectoryService
|
||||
import im.vector.matrix.android.api.session.room.RoomService
|
||||
import im.vector.matrix.android.api.session.signout.SignOutService
|
||||
import im.vector.matrix.android.api.session.sync.FilterService
|
||||
|
@ -33,10 +34,9 @@ import im.vector.matrix.android.internal.session.cache.RealmClearCacheTask
|
|||
import im.vector.matrix.android.internal.session.filter.*
|
||||
import im.vector.matrix.android.internal.session.group.DefaultGroupService
|
||||
import im.vector.matrix.android.internal.session.group.GroupSummaryUpdater
|
||||
import im.vector.matrix.android.internal.session.room.DefaultRoomService
|
||||
import im.vector.matrix.android.internal.session.room.EventRelationsAggregationUpdater
|
||||
import im.vector.matrix.android.internal.session.room.RoomAvatarResolver
|
||||
import im.vector.matrix.android.internal.session.room.RoomSummaryUpdater
|
||||
import im.vector.matrix.android.internal.session.room.*
|
||||
import im.vector.matrix.android.internal.session.room.directory.DefaultGetPublicRoomTask
|
||||
import im.vector.matrix.android.internal.session.room.directory.GetPublicRoomTask
|
||||
import im.vector.matrix.android.internal.session.room.membership.RoomDisplayNameResolver
|
||||
import im.vector.matrix.android.internal.session.room.membership.RoomMemberDisplayNameResolver
|
||||
import im.vector.matrix.android.internal.session.room.prune.EventsPruner
|
||||
|
@ -111,6 +111,14 @@ internal class SessionModule(private val sessionParams: SessionParams) {
|
|||
DefaultRoomService(get(), get(), get(), get()) as RoomService
|
||||
}
|
||||
|
||||
scope(DefaultSession.SCOPE) {
|
||||
DefaultGetPublicRoomTask(get()) as GetPublicRoomTask
|
||||
}
|
||||
|
||||
scope(DefaultSession.SCOPE) {
|
||||
DefaultRoomDirectoryService(get(), get(), get()) as RoomDirectoryService
|
||||
}
|
||||
|
||||
scope(DefaultSession.SCOPE) {
|
||||
DefaultGroupService(get()) as GroupService
|
||||
}
|
||||
|
|
|
@ -21,7 +21,7 @@ import im.vector.matrix.android.api.session.content.ContentUrlResolver
|
|||
|
||||
|
||||
private const val MATRIX_CONTENT_URI_SCHEME = "mxc://"
|
||||
internal const val URI_PREFIX_CONTENT_API = "/_matrix/media/v1/"
|
||||
internal const val URI_PREFIX_CONTENT_API = "_matrix/media/v1/"
|
||||
|
||||
internal class DefaultContentUrlResolver(private val homeServerConnectionConfig: HomeServerConnectionConfig) : ContentUrlResolver {
|
||||
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* 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
|
||||
|
||||
import im.vector.matrix.android.api.MatrixCallback
|
||||
import im.vector.matrix.android.api.session.room.RoomDirectoryService
|
||||
import im.vector.matrix.android.api.session.room.model.roomdirectory.PublicRoomsParams
|
||||
import im.vector.matrix.android.api.session.room.model.roomdirectory.PublicRoomsResponse
|
||||
import im.vector.matrix.android.api.util.Cancelable
|
||||
import im.vector.matrix.android.internal.session.room.directory.GetPublicRoomTask
|
||||
import im.vector.matrix.android.internal.session.room.membership.joining.JoinRoomTask
|
||||
import im.vector.matrix.android.internal.task.TaskExecutor
|
||||
import im.vector.matrix.android.internal.task.configureWith
|
||||
|
||||
internal class DefaultRoomDirectoryService(private val getPublicRoomTask: GetPublicRoomTask,
|
||||
private val joinRoomTask: JoinRoomTask,
|
||||
private val taskExecutor: TaskExecutor) : RoomDirectoryService {
|
||||
|
||||
override fun getPublicRooms(server: String?,
|
||||
publicRoomsParams: PublicRoomsParams,
|
||||
callback: MatrixCallback<PublicRoomsResponse>): Cancelable {
|
||||
return getPublicRoomTask
|
||||
.configureWith(GetPublicRoomTask.Params(server, publicRoomsParams))
|
||||
.dispatchTo(callback)
|
||||
.executeBy(taskExecutor)
|
||||
}
|
||||
|
||||
override fun joinRoom(roomId: String, callback: MatrixCallback<Unit>) {
|
||||
joinRoomTask
|
||||
.configureWith(JoinRoomTask.Params(roomId))
|
||||
.dispatchTo(callback)
|
||||
.executeBy(taskExecutor)
|
||||
}
|
||||
}
|
|
@ -20,6 +20,8 @@ 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.api.session.room.model.roomdirectory.PublicRoomsParams
|
||||
import im.vector.matrix.android.api.session.room.model.roomdirectory.PublicRoomsResponse
|
||||
import im.vector.matrix.android.internal.network.NetworkConstants
|
||||
import im.vector.matrix.android.internal.session.room.membership.RoomMembersResponse
|
||||
import im.vector.matrix.android.internal.session.room.membership.joining.InviteBody
|
||||
|
@ -27,15 +29,21 @@ 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 {
|
||||
|
||||
/**
|
||||
* Lists the public rooms on the server, with optional filter.
|
||||
* This API returns paginated responses. The rooms are ordered by the number of joined members, with the largest rooms first.
|
||||
*
|
||||
* Ref: https://matrix.org/docs/spec/client_server/r0.4.0.html#post-matrix-client-r0-publicrooms
|
||||
*/
|
||||
@POST(NetworkConstants.URI_API_PREFIX_PATH_R0 + "publicRooms")
|
||||
fun publicRooms(@Query("server") server: String?,
|
||||
@Body publicRoomsParams: PublicRoomsParams
|
||||
): Call<PublicRoomsResponse>
|
||||
|
||||
/**
|
||||
* Create a room.
|
||||
* Ref: https://matrix.org/docs/spec/client_server/r0.4.0.html#post-matrix-client-r0-createroom
|
||||
|
@ -213,6 +221,6 @@ internal interface RoomAPI {
|
|||
@Path("txnId") txId: String,
|
||||
@Path("roomId") roomId: String,
|
||||
@Path("eventId") parent_id: String,
|
||||
@Body reason: Map<String, String>
|
||||
@Body reason: Map<String, String>
|
||||
): Call<SendResponse>
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* 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.directory
|
||||
|
||||
import arrow.core.Try
|
||||
import im.vector.matrix.android.api.session.room.model.roomdirectory.PublicRoomsParams
|
||||
import im.vector.matrix.android.api.session.room.model.roomdirectory.PublicRoomsResponse
|
||||
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
|
||||
|
||||
internal interface GetPublicRoomTask : Task<GetPublicRoomTask.Params, PublicRoomsResponse> {
|
||||
data class Params(
|
||||
val server: String?,
|
||||
val publicRoomsParams: PublicRoomsParams
|
||||
)
|
||||
}
|
||||
|
||||
internal class DefaultGetPublicRoomTask(private val roomAPI: RoomAPI) : GetPublicRoomTask {
|
||||
|
||||
override fun execute(params: GetPublicRoomTask.Params): Try<PublicRoomsResponse> {
|
||||
return executeRequest {
|
||||
apiCall = roomAPI.publicRooms(params.server, params.publicRoomsParams)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -43,6 +43,8 @@
|
|||
android:name="im.vector.riotredesign.features.reactions.EmojiReactionPickerActivity"
|
||||
android:label="@string/title_activity_emoji_reaction_picker" />
|
||||
|
||||
<activity android:name=".features.roomdirectory.RoomDirectoryActivity" />
|
||||
|
||||
<service
|
||||
android:name=".core.services.CallService"
|
||||
android:exported="false" />
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
*
|
||||
* * 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.riotredesign.core.epoxy
|
||||
|
||||
import android.widget.Button
|
||||
import android.widget.TextView
|
||||
import com.airbnb.epoxy.EpoxyAttribute
|
||||
import com.airbnb.epoxy.EpoxyModelClass
|
||||
import im.vector.riotredesign.R
|
||||
|
||||
@EpoxyModelClass(layout = R.layout.item_error_retry)
|
||||
abstract class ErrorWithRetryItem : VectorEpoxyModel<ErrorWithRetryItem.Holder>() {
|
||||
|
||||
@EpoxyAttribute
|
||||
var text: String? = null
|
||||
|
||||
@EpoxyAttribute
|
||||
var listener: (() -> Unit)? = null
|
||||
|
||||
override fun bind(holder: Holder) {
|
||||
holder.textView.text = text
|
||||
holder.buttonView.setOnClickListener { listener?.invoke() }
|
||||
}
|
||||
|
||||
|
||||
class Holder : VectorEpoxyHolder() {
|
||||
val textView by bind<TextView>(R.id.itemErrorRetryText)
|
||||
val buttonView by bind<Button>(R.id.itemErrorRetryButton)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
*
|
||||
* * 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.riotredesign.core.epoxy
|
||||
|
||||
import android.widget.TextView
|
||||
import com.airbnb.epoxy.EpoxyAttribute
|
||||
import com.airbnb.epoxy.EpoxyModelClass
|
||||
import im.vector.riotredesign.R
|
||||
|
||||
@EpoxyModelClass(layout = R.layout.item_no_result)
|
||||
abstract class NoResultItem : VectorEpoxyModel<NoResultItem.Holder>() {
|
||||
|
||||
@EpoxyAttribute
|
||||
var text: String? = null
|
||||
|
||||
override fun bind(holder: Holder) {
|
||||
holder.textView.text = text
|
||||
}
|
||||
|
||||
|
||||
class Holder : VectorEpoxyHolder() {
|
||||
val textView by bind<TextView>(R.id.itemNoResultText)
|
||||
}
|
||||
}
|
|
@ -40,6 +40,7 @@ import im.vector.riotredesign.core.platform.VectorBaseActivity
|
|||
import im.vector.riotredesign.features.home.room.detail.LoadingRoomDetailFragment
|
||||
import im.vector.riotredesign.features.rageshake.BugReporter
|
||||
import im.vector.riotredesign.features.rageshake.VectorUncaughtExceptionHandler
|
||||
import im.vector.riotredesign.features.roomdirectory.RoomDirectoryActivity
|
||||
import im.vector.riotredesign.features.settings.VectorSettingsActivity
|
||||
import im.vector.riotredesign.features.workers.signout.SignOutUiWorker
|
||||
import kotlinx.android.synthetic.main.activity_home.*
|
||||
|
@ -124,23 +125,29 @@ class HomeActivity : VectorBaseActivity(), ToolbarConfigurable {
|
|||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
when (item.itemId) {
|
||||
android.R.id.home -> {
|
||||
android.R.id.home -> {
|
||||
drawerLayout.openDrawer(GravityCompat.START)
|
||||
return true
|
||||
}
|
||||
R.id.sliding_menu_settings -> {
|
||||
R.id.sliding_menu_settings -> {
|
||||
startActivity(VectorSettingsActivity.getIntent(this, "TODO"))
|
||||
return true
|
||||
}
|
||||
R.id.sliding_menu_sign_out -> {
|
||||
R.id.sliding_menu_sign_out -> {
|
||||
SignOutUiWorker(this).perform(Matrix.getInstance().currentSession!!)
|
||||
return true
|
||||
}
|
||||
// TODO Temporary code here to create a room
|
||||
R.id.tmp_menu_create_room -> {
|
||||
R.id.tmp_menu_create_room -> {
|
||||
homeActivityViewModel.createRoom()
|
||||
return true
|
||||
}
|
||||
// TODO Temporary code here to open room directory
|
||||
R.id.tmp_menu_open_room_directory -> {
|
||||
// Start Activity for now
|
||||
startActivity(Intent(this, RoomDirectoryActivity::class.java))
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* 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.riotredesign.features.roomdirectory
|
||||
|
||||
import im.vector.riotredesign.R
|
||||
import im.vector.riotredesign.core.extensions.addFragment
|
||||
import im.vector.riotredesign.core.platform.VectorBaseActivity
|
||||
|
||||
class RoomDirectoryActivity : VectorBaseActivity() {
|
||||
|
||||
|
||||
override fun getLayoutRes() = R.layout.activity_simple
|
||||
|
||||
override fun initUiAndData() {
|
||||
if (isFirstCreation()) {
|
||||
addFragment(RoomDirectoryFragment(), R.id.simpleFragmentContainer)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,104 @@
|
|||
/*
|
||||
* 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.riotredesign.features.roomdirectory
|
||||
|
||||
import com.airbnb.epoxy.TypedEpoxyController
|
||||
import com.airbnb.epoxy.VisibilityState
|
||||
import com.airbnb.mvrx.Fail
|
||||
import com.airbnb.mvrx.Incomplete
|
||||
import com.airbnb.mvrx.Success
|
||||
import im.vector.matrix.android.api.session.room.model.roomdirectory.PublicRoom
|
||||
import im.vector.riotredesign.R
|
||||
import im.vector.riotredesign.core.epoxy.errorWithRetryItem
|
||||
import im.vector.riotredesign.core.epoxy.loadingItem
|
||||
import im.vector.riotredesign.core.epoxy.noResultItem
|
||||
import im.vector.riotredesign.core.resources.StringProvider
|
||||
|
||||
class RoomDirectoryController(private val stringProvider: StringProvider) : TypedEpoxyController<RoomDirectoryViewState>() {
|
||||
|
||||
var callback: Callback? = null
|
||||
|
||||
override fun buildModels(viewState: RoomDirectoryViewState) {
|
||||
val publicRooms = viewState.publicRooms
|
||||
|
||||
if (publicRooms.isEmpty()
|
||||
&& viewState.asyncPublicRoomsRequest is Success) {
|
||||
// No result
|
||||
noResultItem {
|
||||
id("noResult")
|
||||
text(stringProvider.getString(R.string.no_result_placeholder))
|
||||
}
|
||||
} else {
|
||||
publicRooms.forEach {
|
||||
buildPublicRoom(it, viewState)
|
||||
}
|
||||
|
||||
if ((viewState.hasMore && viewState.asyncPublicRoomsRequest is Success)
|
||||
|| viewState.asyncPublicRoomsRequest is Incomplete) {
|
||||
loadingItem {
|
||||
// Change id to avoid list to scroll automatically when first results are displayed
|
||||
if (publicRooms.isEmpty()) {
|
||||
id("loading")
|
||||
} else {
|
||||
id("loadMore")
|
||||
}
|
||||
onVisibilityStateChanged { _, _, visibilityState ->
|
||||
if (visibilityState == VisibilityState.VISIBLE) {
|
||||
callback?.loadMore()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (viewState.asyncPublicRoomsRequest is Fail) {
|
||||
errorWithRetryItem {
|
||||
id("error")
|
||||
text(viewState.asyncPublicRoomsRequest.error.localizedMessage)
|
||||
listener { callback?.loadMore() }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun buildPublicRoom(publicRoom: PublicRoom, viewState: RoomDirectoryViewState) {
|
||||
roomDirectoryItem {
|
||||
id(publicRoom.roomId)
|
||||
roomId(publicRoom.roomId)
|
||||
avatarUrl(publicRoom.avatarUrl)
|
||||
roomName(publicRoom.name)
|
||||
nbOfMembers(publicRoom.numJoinedMembers)
|
||||
when {
|
||||
viewState.joinedRoomsIds.contains(publicRoom.roomId) -> joinState(RoomDirectoryItem.JoinState.JOINED)
|
||||
viewState.joiningRoomsIds.contains(publicRoom.roomId) -> joinState(RoomDirectoryItem.JoinState.JOINING)
|
||||
else -> joinState(RoomDirectoryItem.JoinState.NOT_JOINED)
|
||||
}
|
||||
joinListener {
|
||||
callback?.onPublicRoomJoin(publicRoom)
|
||||
}
|
||||
globalListener {
|
||||
callback?.onPublicRoomClicked(publicRoom)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interface Callback {
|
||||
fun onPublicRoomClicked(publicRoom: PublicRoom)
|
||||
fun onPublicRoomJoin(publicRoom: PublicRoom)
|
||||
fun loadMore()
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,113 @@
|
|||
/*
|
||||
* 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.riotredesign.features.roomdirectory
|
||||
|
||||
import android.os.Bundle
|
||||
import android.text.Editable
|
||||
import android.view.View
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.airbnb.epoxy.EpoxyVisibilityTracker
|
||||
import com.airbnb.mvrx.fragmentViewModel
|
||||
import com.airbnb.mvrx.withState
|
||||
import im.vector.matrix.android.api.session.room.model.roomdirectory.PublicRoom
|
||||
import im.vector.riotredesign.R
|
||||
import im.vector.riotredesign.core.platform.SimpleTextWatcher
|
||||
import im.vector.riotredesign.core.platform.VectorBaseFragment
|
||||
import kotlinx.android.synthetic.main.fragment_room_directory.*
|
||||
import org.koin.android.ext.android.get
|
||||
import timber.log.Timber
|
||||
|
||||
|
||||
/**
|
||||
* What can be improved:
|
||||
* - When filtering more (when entering new chars), we could filter on result we already have, during the new server request, to avoid empty screen effect
|
||||
*
|
||||
* FIXME Rotate screen launch again the request
|
||||
*
|
||||
* For Nad:
|
||||
* Display number of rooms?
|
||||
* Picto size are not correct
|
||||
* Where I put the room directory picker?
|
||||
*
|
||||
*/
|
||||
class RoomDirectoryFragment : VectorBaseFragment(), RoomDirectoryController.Callback {
|
||||
|
||||
private val viewModel: RoomDirectoryViewModel by fragmentViewModel()
|
||||
|
||||
private val roomDirectoryController = RoomDirectoryController(get())
|
||||
|
||||
override fun getLayoutResId() = R.layout.fragment_room_directory
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
vectorBaseActivity.setSupportActionBar(toolbar)
|
||||
|
||||
vectorBaseActivity.supportActionBar?.let {
|
||||
it.setDisplayShowHomeEnabled(true)
|
||||
it.setDisplayHomeAsUpEnabled(true)
|
||||
}
|
||||
|
||||
roomDirectoryFilter.addTextChangedListener(object : SimpleTextWatcher() {
|
||||
override fun afterTextChanged(s: Editable) {
|
||||
// TODO Debounce
|
||||
viewModel.filterWith(roomDirectoryFilter.text.toString())
|
||||
}
|
||||
})
|
||||
|
||||
createNewRoom.setOnClickListener {
|
||||
vectorBaseActivity.notImplemented()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
||||
super.onActivityCreated(savedInstanceState)
|
||||
|
||||
setupRecyclerView()
|
||||
}
|
||||
|
||||
private fun setupRecyclerView() {
|
||||
val epoxyVisibilityTracker = EpoxyVisibilityTracker()
|
||||
epoxyVisibilityTracker.attach(roomDirectoryList)
|
||||
|
||||
val layoutManager = LinearLayoutManager(context)
|
||||
|
||||
roomDirectoryList.layoutManager = layoutManager
|
||||
roomDirectoryController.callback = this
|
||||
|
||||
roomDirectoryList.setController(roomDirectoryController)
|
||||
}
|
||||
|
||||
override fun onPublicRoomClicked(publicRoom: PublicRoom) {
|
||||
Timber.v("PublicRoomClicked: $publicRoom")
|
||||
vectorBaseActivity.notImplemented()
|
||||
}
|
||||
|
||||
override fun onPublicRoomJoin(publicRoom: PublicRoom) {
|
||||
Timber.v("PublicRoomJoinClicked: $publicRoom")
|
||||
viewModel.joinRoom(publicRoom)
|
||||
}
|
||||
|
||||
override fun loadMore() {
|
||||
viewModel.loadMore()
|
||||
}
|
||||
|
||||
override fun invalidate() = withState(viewModel) { state ->
|
||||
// Populate list with Epoxy
|
||||
roomDirectoryController.setData(state)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,97 @@
|
|||
/*
|
||||
* 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.riotredesign.features.roomdirectory
|
||||
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.ImageView
|
||||
import android.widget.TextView
|
||||
import androidx.core.view.isInvisible
|
||||
import androidx.core.view.isVisible
|
||||
import com.airbnb.epoxy.EpoxyAttribute
|
||||
import com.airbnb.epoxy.EpoxyModelClass
|
||||
import im.vector.riotredesign.R
|
||||
import im.vector.riotredesign.core.epoxy.VectorEpoxyHolder
|
||||
import im.vector.riotredesign.core.epoxy.VectorEpoxyModel
|
||||
import im.vector.riotredesign.features.home.AvatarRenderer
|
||||
|
||||
@EpoxyModelClass(layout = R.layout.item_room_directory)
|
||||
abstract class RoomDirectoryItem : VectorEpoxyModel<RoomDirectoryItem.Holder>() {
|
||||
|
||||
// TODO Manage join state waiting from the sync
|
||||
enum class JoinState {
|
||||
NOT_JOINED,
|
||||
JOINING,
|
||||
JOINED
|
||||
}
|
||||
|
||||
@EpoxyAttribute
|
||||
var avatarUrl: String? = null
|
||||
|
||||
@EpoxyAttribute
|
||||
var roomId: String? = null
|
||||
|
||||
@EpoxyAttribute
|
||||
var roomName: String? = null
|
||||
|
||||
@EpoxyAttribute
|
||||
var nbOfMembers: Int = 0
|
||||
|
||||
@EpoxyAttribute
|
||||
var joinState: JoinState = JoinState.NOT_JOINED
|
||||
|
||||
@EpoxyAttribute
|
||||
var globalListener: (() -> Unit)? = null
|
||||
|
||||
@EpoxyAttribute
|
||||
var joinListener: (() -> Unit)? = null
|
||||
|
||||
override fun bind(holder: Holder) {
|
||||
holder.rootView.setOnClickListener { globalListener?.invoke() }
|
||||
|
||||
AvatarRenderer.render(avatarUrl, roomId!!, roomName, holder.avatarView)
|
||||
holder.nameView.text = roomName
|
||||
// TODO Use formatter for big numbers?
|
||||
holder.counterView.text = nbOfMembers.toString()
|
||||
|
||||
if (joinState == JoinState.NOT_JOINED) {
|
||||
holder.joinButton.isVisible = true
|
||||
} else {
|
||||
// We use isInvisible because we want to keep button space in the layout
|
||||
holder.joinButton.isInvisible = true
|
||||
}
|
||||
holder.joiningView.isVisible = joinState == JoinState.JOINING
|
||||
holder.joinedView.isVisible = joinState == JoinState.JOINED
|
||||
|
||||
holder.joinButton.setOnClickListener { joinListener?.invoke() }
|
||||
}
|
||||
|
||||
|
||||
class Holder : VectorEpoxyHolder() {
|
||||
val rootView by bind<ViewGroup>(R.id.itemRoomDirectoryLayout)
|
||||
|
||||
val avatarView by bind<ImageView>(R.id.itemRoomDirectoryAvatar)
|
||||
val nameView by bind<TextView>(R.id.itemRoomDirectoryName)
|
||||
val counterView by bind<TextView>(R.id.itemRoomDirectoryMembersCount)
|
||||
|
||||
val joinedView by bind<View>(R.id.itemRoomDirectoryJoined)
|
||||
val joinButton by bind<View>(R.id.itemRoomDirectoryJoin)
|
||||
val joiningView by bind<View>(R.id.itemRoomDirectoryJoining)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,184 @@
|
|||
/*
|
||||
* 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.riotredesign.features.roomdirectory
|
||||
|
||||
import com.airbnb.mvrx.*
|
||||
import im.vector.matrix.android.api.MatrixCallback
|
||||
import im.vector.matrix.android.api.session.Session
|
||||
import im.vector.matrix.android.api.session.room.model.Membership
|
||||
import im.vector.matrix.android.api.session.room.model.roomdirectory.PublicRoom
|
||||
import im.vector.matrix.android.api.session.room.model.roomdirectory.PublicRoomsFilter
|
||||
import im.vector.matrix.android.api.session.room.model.roomdirectory.PublicRoomsParams
|
||||
import im.vector.matrix.android.api.session.room.model.roomdirectory.PublicRoomsResponse
|
||||
import im.vector.matrix.android.api.util.Cancelable
|
||||
import im.vector.matrix.rx.rx
|
||||
import im.vector.riotredesign.core.platform.VectorViewModel
|
||||
import org.koin.android.ext.android.get
|
||||
import timber.log.Timber
|
||||
|
||||
private const val PUBLIC_ROOMS_LIMIT = 20
|
||||
|
||||
class RoomDirectoryViewModel(initialState: RoomDirectoryViewState,
|
||||
private val session: Session) : VectorViewModel<RoomDirectoryViewState>(initialState) {
|
||||
|
||||
companion object : MvRxViewModelFactory<RoomDirectoryViewModel, RoomDirectoryViewState> {
|
||||
|
||||
@JvmStatic
|
||||
override fun create(viewModelContext: ViewModelContext, state: RoomDirectoryViewState): RoomDirectoryViewModel? {
|
||||
val currentSession = viewModelContext.activity.get<Session>()
|
||||
|
||||
return RoomDirectoryViewModel(state, currentSession)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO Store in ViewState?
|
||||
private var currentFilter: String = ""
|
||||
|
||||
private var since: String? = null
|
||||
|
||||
private var currentTask: Cancelable? = null
|
||||
|
||||
init {
|
||||
// Load with empty filter
|
||||
load()
|
||||
|
||||
// Observe joined room (from the sync)
|
||||
observeJoinedRooms()
|
||||
}
|
||||
|
||||
private fun observeJoinedRooms() {
|
||||
session
|
||||
.rx()
|
||||
.liveRoomSummaries()
|
||||
.execute { async ->
|
||||
val joinedRoomIds = async.invoke()
|
||||
?.filter {
|
||||
// Keep only joined room
|
||||
it.membership == Membership.JOIN
|
||||
}
|
||||
?.map {
|
||||
it.roomId
|
||||
}
|
||||
?.toList()
|
||||
?: emptyList()
|
||||
|
||||
|
||||
copy(joinedRoomsIds = joinedRoomIds,
|
||||
// Remove (newly) joined room id from the joining room list
|
||||
joiningRoomsIds = joiningRoomsIds.toMutableList().apply { removeAll(joinedRoomIds) }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun filterWith(filter: String) {
|
||||
currentTask?.cancel()
|
||||
|
||||
currentFilter = filter
|
||||
|
||||
// Reset since token
|
||||
since = null
|
||||
|
||||
setState {
|
||||
copy(
|
||||
publicRooms = emptyList(),
|
||||
asyncPublicRoomsRequest = Loading(),
|
||||
hasMore = false)
|
||||
}
|
||||
|
||||
load()
|
||||
}
|
||||
|
||||
fun loadMore() {
|
||||
if (currentTask == null) {
|
||||
setState {
|
||||
copy(
|
||||
asyncPublicRoomsRequest = Loading()
|
||||
)
|
||||
}
|
||||
|
||||
load()
|
||||
}
|
||||
}
|
||||
|
||||
private fun load() {
|
||||
currentTask = session.getPublicRooms(null, // TODO session.sessionParams.homeServerConnectionConfig.homeServerUri.toString(),
|
||||
PublicRoomsParams(
|
||||
limit = PUBLIC_ROOMS_LIMIT,
|
||||
filter = PublicRoomsFilter(searchTerm = currentFilter),
|
||||
includeAllNetworks = false, // TODO
|
||||
since = since,
|
||||
thirdPartyInstanceId = null // TODO
|
||||
),
|
||||
object : MatrixCallback<PublicRoomsResponse> {
|
||||
override fun onSuccess(data: PublicRoomsResponse) {
|
||||
currentTask = null
|
||||
|
||||
since = data.nextBatch
|
||||
|
||||
setState {
|
||||
copy(
|
||||
asyncPublicRoomsRequest = Success(data.chunk!!),
|
||||
// It's ok to append at the end of the list, so I use publicRooms.size()
|
||||
publicRooms = publicRooms.appendAt(data.chunk!!, publicRooms.size),
|
||||
hasMore = since != null
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onFailure(failure: Throwable) {
|
||||
currentTask = null
|
||||
|
||||
setState {
|
||||
copy(
|
||||
asyncPublicRoomsRequest = Fail(failure)
|
||||
)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fun joinRoom(publicRoom: PublicRoom) = withState { state ->
|
||||
if (state.joiningRoomsIds.contains(publicRoom.roomId)) {
|
||||
// Request already sent, should not happen
|
||||
Timber.w("Try to join an already joining room. Should not happen")
|
||||
return@withState
|
||||
}
|
||||
|
||||
setState {
|
||||
copy(
|
||||
joiningRoomsIds = joiningRoomsIds.toMutableList().apply { add(publicRoom.roomId) }
|
||||
)
|
||||
}
|
||||
|
||||
session.joinRoom(publicRoom.roomId, object : MatrixCallback<Unit> {
|
||||
override fun onSuccess(data: Unit) {
|
||||
// We do not update the joiningRoomsIds here, because, the room is not joined yet regarding the sync data.
|
||||
// Instead, we wait for the room to be joined
|
||||
}
|
||||
|
||||
override fun onFailure(failure: Throwable) {
|
||||
// TODO Notify the user
|
||||
setState {
|
||||
copy(
|
||||
joiningRoomsIds = joiningRoomsIds.toMutableList().apply { remove(publicRoom.roomId) }
|
||||
)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* 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.riotredesign.features.roomdirectory
|
||||
|
||||
import com.airbnb.mvrx.Async
|
||||
import com.airbnb.mvrx.MvRxState
|
||||
import com.airbnb.mvrx.Uninitialized
|
||||
import im.vector.matrix.android.api.session.room.model.roomdirectory.PublicRoom
|
||||
|
||||
data class RoomDirectoryViewState(
|
||||
// Store cumul of pagination result
|
||||
val publicRooms: List<PublicRoom> = emptyList(),
|
||||
// Current pagination request
|
||||
val asyncPublicRoomsRequest: Async<List<PublicRoom>> = Uninitialized,
|
||||
// True if more result are available server side
|
||||
val hasMore: Boolean = false,
|
||||
// List of roomIds that the user wants to join
|
||||
val joiningRoomsIds: List<String> = emptyList(),
|
||||
// List of joined roomId,
|
||||
val joinedRoomsIds: List<String> = emptyList()
|
||||
) : MvRxState
|
13
vector/src/main/res/drawable/ic_plus_circle.xml
Normal file
13
vector/src/main/res/drawable/ic_plus_circle.xml
Normal file
|
@ -0,0 +1,13 @@
|
|||
<vector android:height="24dp" android:viewportHeight="22"
|
||||
android:viewportWidth="22" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="#00000000" android:fillType="evenOdd"
|
||||
android:pathData="M11,11m-10,0a10,10 0,1 1,20 0a10,10 0,1 1,-20 0"
|
||||
android:strokeColor="#03B381" android:strokeLineCap="round"
|
||||
android:strokeLineJoin="round" android:strokeWidth="1.4"/>
|
||||
<path android:fillColor="#00000000" android:fillType="evenOdd"
|
||||
android:pathData="M11,7L11,15" android:strokeColor="#03B381"
|
||||
android:strokeLineCap="round" android:strokeLineJoin="round" android:strokeWidth="1.4"/>
|
||||
<path android:fillColor="#00000000" android:fillType="evenOdd"
|
||||
android:pathData="M7,11L15,11" android:strokeColor="#03B381"
|
||||
android:strokeLineCap="round" android:strokeLineJoin="round" android:strokeWidth="1.4"/>
|
||||
</vector>
|
9
vector/src/main/res/drawable/ic_tick.xml
Normal file
9
vector/src/main/res/drawable/ic_tick.xml
Normal file
|
@ -0,0 +1,9 @@
|
|||
<vector android:height="24dp" android:viewportHeight="11"
|
||||
android:viewportWidth="15" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="#00000000" android:fillType="evenOdd"
|
||||
android:pathData="M5.3033,10.0815L13.7886,1.5962"
|
||||
android:strokeColor="#7E899C" android:strokeLineCap="round" android:strokeWidth="1.3"/>
|
||||
<path android:fillColor="#00000000" android:fillType="evenOdd"
|
||||
android:pathData="M5.3033,10.0815L1.0607,5.8388"
|
||||
android:strokeColor="#7E899C" android:strokeLineCap="round" android:strokeWidth="1.3"/>
|
||||
</vector>
|
11
vector/src/main/res/drawable/ic_user.xml
Normal file
11
vector/src/main/res/drawable/ic_user.xml
Normal file
|
@ -0,0 +1,11 @@
|
|||
<vector android:height="24dp" android:viewportHeight="16"
|
||||
android:viewportWidth="15" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="#00000000" android:fillType="evenOdd"
|
||||
android:pathData="M14,15L14,13.4444C14,11.7262 12.5449,10.3333 10.75,10.3333L4.25,10.3333C2.4551,10.3333 1,11.7262 1,13.4444L1,15"
|
||||
android:strokeColor="#7E899C" android:strokeLineCap="round"
|
||||
android:strokeLineJoin="round" android:strokeWidth="1.16666667"/>
|
||||
<path android:fillColor="#00000000" android:fillType="evenOdd"
|
||||
android:pathData="M4.25,4.1111a3.25,3.1111 0,1 0,6.5 0a3.25,3.1111 0,1 0,-6.5 0z"
|
||||
android:strokeColor="#7E899C" android:strokeLineCap="round"
|
||||
android:strokeLineJoin="round" android:strokeWidth="1.16666667"/>
|
||||
</vector>
|
5
vector/src/main/res/layout/activity_simple.xml
Normal file
5
vector/src/main/res/layout/activity_simple.xml
Normal file
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/simpleFragmentContainer"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
65
vector/src/main/res/layout/fragment_room_directory.xml
Normal file
65
vector/src/main/res/layout/fragment_room_directory.xml
Normal file
|
@ -0,0 +1,65 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/pale_grey">
|
||||
|
||||
<com.airbnb.epoxy.EpoxyRecyclerView
|
||||
android:id="@+id/roomDirectoryList"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_behavior="@string/appbar_scrolling_view_behavior"
|
||||
tools:listitem="@layout/item_room_directory" />
|
||||
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<com.google.android.material.appbar.CollapsingToolbarLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:expandedTitleGravity="top"
|
||||
app:layout_scrollFlags="scroll|enterAlways|exitUntilCollapsed|snap">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<androidx.appcompat.widget.Toolbar
|
||||
android:id="@+id/toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<EditText
|
||||
android:id="@+id/roomDirectoryFilter"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="@dimen/layout_horizontal_margin"
|
||||
android:layout_marginRight="@dimen/layout_horizontal_margin"
|
||||
android:hint="@string/home_filter_placeholder_rooms" />
|
||||
|
||||
</androidx.appcompat.widget.Toolbar>
|
||||
|
||||
<Button
|
||||
android:id="@+id/createNewRoom"
|
||||
style="@style/VectorButtonStyleFlat"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="48dp"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginLeft="16dp"
|
||||
android:drawableStart="@drawable/ic_plus_circle"
|
||||
android:drawableLeft="@drawable/ic_plus_circle"
|
||||
android:drawablePadding="13dp"
|
||||
android:text="@string/create_new_room" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</com.google.android.material.appbar.CollapsingToolbarLayout>
|
||||
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
30
vector/src/main/res/layout/item_error_retry.xml
Normal file
30
vector/src/main/res/layout/item_error_retry.xml
Normal file
|
@ -0,0 +1,30 @@
|
|||
<?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:id="@+id/progressBar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="16dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/itemErrorRetryText"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:text="Error" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/itemErrorRetryButton"
|
||||
style="@style/VectorButtonStyle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="4dp"
|
||||
android:text="@string/global_retry"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/itemErrorRetryText" />
|
||||
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
8
vector/src/main/res/layout/item_no_result.xml
Normal file
8
vector/src/main/res/layout/item_no_result.xml
Normal file
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/itemNoResultText"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:padding="16dp"
|
||||
android:text="@string/no_result_placeholder" />
|
105
vector/src/main/res/layout/item_room_directory.xml
Normal file
105
vector/src/main/res/layout/item_room_directory.xml
Normal file
|
@ -0,0 +1,105 @@
|
|||
<?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:id="@+id/itemRoomDirectoryLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/bg_room_item"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
android:foreground="?attr/selectableItemBackground">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/itemRoomDirectoryAvatar"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginLeft="8dp"
|
||||
android:layout_marginTop="4dp"
|
||||
android:layout_marginBottom="4dp"
|
||||
app:layout_constraintBottom_toTopOf="@+id/itemRoomDirectoryBottomSeparator"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:src="@tools:sample/avatars" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/itemRoomDirectoryName"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="2"
|
||||
android:textColor="#2E2F3E"
|
||||
android:textSize="15sp"
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintBottom_toTopOf="@+id/itemRoomDirectoryBottomSeparator"
|
||||
app:layout_constraintEnd_toStartOf="@id/itemRoomDirectoryMembersCount"
|
||||
app:layout_constraintStart_toEndOf="@id/itemRoomDirectoryAvatar"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:text="@tools:sample/lorem/random" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/itemRoomDirectoryMembersCount"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:layout_marginRight="8dp"
|
||||
android:drawableStart="@drawable/ic_user"
|
||||
android:drawableLeft="@drawable/ic_user"
|
||||
android:drawablePadding="8dp"
|
||||
android:gravity="center_vertical"
|
||||
android:minWidth="40dp"
|
||||
android:textColor="#7E899C"
|
||||
android:textSize="15sp"
|
||||
app:layout_constraintBottom_toTopOf="@+id/itemRoomDirectoryBottomSeparator"
|
||||
app:layout_constraintEnd_toStartOf="@id/itemRoomDirectoryJoin"
|
||||
app:layout_constraintStart_toEndOf="@id/itemRoomDirectoryName"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:text="148" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/itemRoomDirectoryJoin"
|
||||
style="@style/VectorButtonStyleFlat"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:layout_marginRight="8dp"
|
||||
android:text="@string/join"
|
||||
app:layout_constraintBottom_toTopOf="@+id/itemRoomDirectoryBottomSeparator"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/itemRoomDirectoryJoined"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:scaleType="center"
|
||||
android:src="@drawable/ic_tick"
|
||||
app:layout_constraintBottom_toTopOf="@+id/itemRoomDirectoryBottomSeparator"
|
||||
app:layout_constraintEnd_toEndOf="@+id/itemRoomDirectoryJoin"
|
||||
app:layout_constraintStart_toStartOf="@+id/itemRoomDirectoryJoin"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/itemRoomDirectoryJoining"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="24dp"
|
||||
android:scaleType="center"
|
||||
app:layout_constraintBottom_toTopOf="@+id/itemRoomDirectoryBottomSeparator"
|
||||
app:layout_constraintEnd_toEndOf="@+id/itemRoomDirectoryJoin"
|
||||
app:layout_constraintStart_toStartOf="@+id/itemRoomDirectoryJoin"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<View
|
||||
android:id="@+id/itemRoomDirectoryBottomSeparator"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="1dp"
|
||||
android:background="#E9EDF1"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
@ -16,4 +16,9 @@
|
|||
android:id="@+id/tmp_menu_create_room"
|
||||
android:title="@string/room_recents_create_room" />
|
||||
|
||||
<!-- TODO Temporary code -->
|
||||
<item
|
||||
android:id="@+id/tmp_menu_open_room_directory"
|
||||
android:title="Open room directory" />
|
||||
|
||||
</menu>
|
|
@ -17,4 +17,7 @@
|
|||
<string name="event_redacted_by_admin_reason">Event moderated by room admin</string>
|
||||
<string name="last_edited_info_message">Last edited by %s on %s</string>
|
||||
|
||||
|
||||
<string name="create_new_room">Create New Room</string>
|
||||
|
||||
</resources>
|
Loading…
Reference in a new issue