forked from GitHub-Mirror/riotX-android
RoomDirectoryPicker WIP
This commit is contained in:
parent
877de1f597
commit
2404eeadf0
@ -19,6 +19,7 @@ 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.session.room.model.thirdparty.ThirdPartyProtocol
|
||||
import im.vector.matrix.android.api.util.Cancelable
|
||||
|
||||
/**
|
||||
@ -39,4 +40,10 @@ interface RoomDirectoryService {
|
||||
fun joinRoom(roomId: String,
|
||||
callback: MatrixCallback<Unit>)
|
||||
|
||||
/**
|
||||
* Fetches the overall metadata about protocols supported by the homeserver.
|
||||
* Includes both the available protocols and all fields required for queries against each protocol.
|
||||
*/
|
||||
fun getThirdPartyProtocol(callback: MatrixCallback<Map<String, ThirdPartyProtocol>>)
|
||||
|
||||
}
|
@ -46,12 +46,12 @@ data class PublicRoomsParams(
|
||||
/**
|
||||
* Whether or not to include all known networks/protocols from application services on the homeserver. Defaults to false.
|
||||
*/
|
||||
@Json(name = "includeAllNetworks")
|
||||
@Json(name = "include_all_networks")
|
||||
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")
|
||||
@Json(name = "third_party_instance_id")
|
||||
var thirdPartyInstanceId: String? = null
|
||||
)
|
||||
|
@ -0,0 +1,36 @@
|
||||
/*
|
||||
* 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.thirdparty
|
||||
|
||||
import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.JsonClass
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class FieldType(
|
||||
/**
|
||||
* Required. A regular expression for validation of a field's value. This may be relatively coarse to verify the value as the application
|
||||
* service providing this protocol may apply additional
|
||||
*/
|
||||
@Json(name = "regexp")
|
||||
val regexp: String? = null,
|
||||
|
||||
/**
|
||||
* Required. An placeholder serving as a valid example of the field value.
|
||||
*/
|
||||
@Json(name = "placeholder")
|
||||
val placeholder: String? = null
|
||||
)
|
@ -0,0 +1,55 @@
|
||||
/*
|
||||
* 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.thirdparty
|
||||
|
||||
/**
|
||||
* This class describes a rooms directory server.
|
||||
*/
|
||||
data class RoomDirectoryData(
|
||||
|
||||
/**
|
||||
* The server name (might be null)
|
||||
* Set null when the server is the current user's home server.
|
||||
*/
|
||||
val homeServer: String? = null,
|
||||
|
||||
/**
|
||||
* The display name (the server description)
|
||||
*/
|
||||
val displayName: String = DEFAULT_HOME_SERVER_NAME,
|
||||
|
||||
/**
|
||||
* The third party server identifier
|
||||
*/
|
||||
val thirdPartyInstanceId: String? = null,
|
||||
|
||||
/**
|
||||
* Tell if all the federated servers must be included
|
||||
*/
|
||||
val includeAllNetworks: Boolean = false,
|
||||
|
||||
/**
|
||||
* the avatar url
|
||||
*/
|
||||
val avatarUrl: String? = null
|
||||
) {
|
||||
|
||||
companion object {
|
||||
const val DEFAULT_HOME_SERVER_NAME = "Matrix"
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,62 @@
|
||||
/*
|
||||
* 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.thirdparty
|
||||
|
||||
import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.JsonClass
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class ThirdPartyProtocol(
|
||||
/**
|
||||
* Required. Fields which may be used to identify a third party user. These should be ordered to suggest the way that entities may be grouped,
|
||||
* where higher groupings are ordered first. For example, the name of a network should be searched before the nickname of a user.
|
||||
*/
|
||||
@Json(name = "user_fields")
|
||||
var userFields: List<String>? = null,
|
||||
|
||||
/**
|
||||
* Required. Fields which may be used to identify a third party location. These should be ordered to suggest the way that
|
||||
* entities may be grouped, where higher groupings are ordered first. For example, the name of a network should be
|
||||
* searched before the name of a channel.
|
||||
*/
|
||||
@Json(name = "location_fields")
|
||||
var locationFields: List<String>? = null,
|
||||
|
||||
/**
|
||||
* Required. A content URI representing an icon for the third party protocol.
|
||||
*
|
||||
* FIXDOC: This field was not present in legacy Riot, and it is sometimes sent by the server (no not Required?)
|
||||
*/
|
||||
@Json(name = "icon")
|
||||
var icon: String? = null,
|
||||
|
||||
/**
|
||||
* Required. The type definitions for the fields defined in the user_fields and location_fields. Each entry in those arrays MUST have an entry here.
|
||||
* The string key for this object is field name itself.
|
||||
*
|
||||
* May be an empty object if no fields are defined.
|
||||
*/
|
||||
@Json(name = "field_types")
|
||||
var fieldTypes: Map<String, FieldType>? = null,
|
||||
|
||||
/**
|
||||
* Required. A list of objects representing independent instances of configuration. For example, multiple networks on IRC
|
||||
* if multiple are provided by the same application service.
|
||||
*/
|
||||
@Json(name = "instances")
|
||||
var instances: List<ThirdPartyProtocolInstance>? = null
|
||||
)
|
@ -0,0 +1,60 @@
|
||||
/*
|
||||
* 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.thirdparty
|
||||
|
||||
import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.JsonClass
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class ThirdPartyProtocolInstance(
|
||||
/**
|
||||
* Required. A human-readable description for the protocol, such as the name.
|
||||
*/
|
||||
@Json(name = "desc")
|
||||
var desc: String? = null,
|
||||
|
||||
/**
|
||||
* An optional content URI representing the protocol. Overrides the one provided at the higher level Protocol object.
|
||||
*/
|
||||
@Json(name = "icon")
|
||||
var icon: String? = null,
|
||||
|
||||
/**
|
||||
* Required. Preset values for fields the client may use to search by.
|
||||
*/
|
||||
@Json(name = "fields")
|
||||
var fields: Map<String, Any>? = null,
|
||||
|
||||
/**
|
||||
* Required. A unique identifier across all instances.
|
||||
*/
|
||||
@Json(name = "network_id")
|
||||
var networkId: String? = null,
|
||||
|
||||
/**
|
||||
* FIXDOC Not documented on matrix.org doc
|
||||
*/
|
||||
@Json(name = "instance_id")
|
||||
var instanceId: String? = null,
|
||||
|
||||
|
||||
/**
|
||||
* FIXDOC Not documented on matrix.org doc
|
||||
*/
|
||||
@Json(name = "bot_user_id")
|
||||
var botUserId: String? = null
|
||||
)
|
@ -36,6 +36,7 @@ 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.room.model.thirdparty.ThirdPartyProtocol
|
||||
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
|
||||
@ -188,6 +189,11 @@ internal class DefaultSession(override val sessionParams: SessionParams) : Sessi
|
||||
return roomDirectoryService.joinRoom(roomId, callback)
|
||||
}
|
||||
|
||||
override fun getThirdPartyProtocol(callback: MatrixCallback<Map<String, ThirdPartyProtocol>>) {
|
||||
assert(isOpen)
|
||||
return roomDirectoryService.getThirdPartyProtocol(callback)
|
||||
}
|
||||
|
||||
// GROUP SERVICE
|
||||
|
||||
override fun getGroup(groupId: String): Group? {
|
||||
|
@ -36,7 +36,9 @@ 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.*
|
||||
import im.vector.matrix.android.internal.session.room.directory.DefaultGetPublicRoomTask
|
||||
import im.vector.matrix.android.internal.session.room.directory.DefaultGetThirdPartyProtocolsTask
|
||||
import im.vector.matrix.android.internal.session.room.directory.GetPublicRoomTask
|
||||
import im.vector.matrix.android.internal.session.room.directory.GetThirdPartyProtocolsTask
|
||||
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
|
||||
@ -116,7 +118,11 @@ internal class SessionModule(private val sessionParams: SessionParams) {
|
||||
}
|
||||
|
||||
scope(DefaultSession.SCOPE) {
|
||||
DefaultRoomDirectoryService(get(), get(), get()) as RoomDirectoryService
|
||||
DefaultGetThirdPartyProtocolsTask(get()) as GetThirdPartyProtocolsTask
|
||||
}
|
||||
|
||||
scope(DefaultSession.SCOPE) {
|
||||
DefaultRoomDirectoryService(get(), get(), get(), get()) as RoomDirectoryService
|
||||
}
|
||||
|
||||
scope(DefaultSession.SCOPE) {
|
||||
|
@ -20,14 +20,17 @@ 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.session.room.model.thirdparty.ThirdPartyProtocol
|
||||
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.directory.GetThirdPartyProtocolsTask
|
||||
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 getThirdPartyProtocolsTask: GetThirdPartyProtocolsTask,
|
||||
private val taskExecutor: TaskExecutor) : RoomDirectoryService {
|
||||
|
||||
override fun getPublicRooms(server: String?,
|
||||
@ -45,4 +48,11 @@ internal class DefaultRoomDirectoryService(private val getPublicRoomTask: GetPub
|
||||
.dispatchTo(callback)
|
||||
.executeBy(taskExecutor)
|
||||
}
|
||||
|
||||
override fun getThirdPartyProtocol(callback: MatrixCallback<Map<String, ThirdPartyProtocol>>) {
|
||||
getThirdPartyProtocolsTask
|
||||
.configureWith(Unit)
|
||||
.dispatchTo(callback)
|
||||
.executeBy(taskExecutor)
|
||||
}
|
||||
}
|
@ -25,6 +25,7 @@ import im.vector.matrix.android.api.session.room.model.roomdirectory.PublicRooms
|
||||
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
|
||||
import im.vector.matrix.android.api.session.room.model.thirdparty.ThirdPartyProtocol
|
||||
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
|
||||
@ -33,6 +34,14 @@ import retrofit2.http.*
|
||||
|
||||
internal interface RoomAPI {
|
||||
|
||||
/**
|
||||
* Get the third party server protocols.
|
||||
*
|
||||
* Ref: https://matrix.org/docs/spec/client_server/r0.4.0.html#get-matrix-client-r0-thirdparty-protocols
|
||||
*/
|
||||
@GET(NetworkConstants.URI_API_PREFIX_PATH_R0 + "thirdparty/protocols")
|
||||
fun thirdPartyProtocols(): Call<Map<String, ThirdPartyProtocol>>
|
||||
|
||||
/**
|
||||
* 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.
|
||||
|
@ -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.matrix.android.internal.session.room.directory
|
||||
|
||||
import arrow.core.Try
|
||||
import im.vector.matrix.android.api.session.room.model.thirdparty.ThirdPartyProtocol
|
||||
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 GetThirdPartyProtocolsTask : Task<Unit, Map<String, ThirdPartyProtocol>>
|
||||
|
||||
internal class DefaultGetThirdPartyProtocolsTask(private val roomAPI: RoomAPI) : GetThirdPartyProtocolsTask {
|
||||
|
||||
override fun execute(params: Unit): Try<Map<String, ThirdPartyProtocol>> {
|
||||
return executeRequest {
|
||||
apiCall = roomAPI.thirdPartyProtocols()
|
||||
}
|
||||
}
|
||||
}
|
@ -20,6 +20,7 @@ import android.content.Context
|
||||
import android.content.Context.MODE_PRIVATE
|
||||
import im.vector.matrix.android.api.Matrix
|
||||
import im.vector.riotredesign.core.resources.LocaleProvider
|
||||
import im.vector.riotredesign.core.resources.StringArrayProvider
|
||||
import im.vector.riotredesign.core.resources.StringProvider
|
||||
import im.vector.riotredesign.features.home.group.SelectedGroupStore
|
||||
import im.vector.riotredesign.features.home.room.VisibleRoomStore
|
||||
@ -40,6 +41,10 @@ class AppModule(private val context: Context) {
|
||||
StringProvider(context.resources)
|
||||
}
|
||||
|
||||
single {
|
||||
StringArrayProvider(context.resources)
|
||||
}
|
||||
|
||||
single {
|
||||
context.getSharedPreferences("im.vector.riot", MODE_PRIVATE)
|
||||
}
|
||||
|
@ -18,26 +18,26 @@ package im.vector.riotredesign.core.extensions
|
||||
|
||||
import androidx.fragment.app.Fragment
|
||||
|
||||
fun androidx.fragment.app.Fragment.addFragment(fragment: Fragment, frameId: Int) {
|
||||
fun Fragment.addFragment(fragment: Fragment, frameId: Int) {
|
||||
fragmentManager?.inTransaction { add(frameId, fragment) }
|
||||
}
|
||||
|
||||
fun androidx.fragment.app.Fragment.replaceFragment(fragment: Fragment, frameId: Int) {
|
||||
fun Fragment.replaceFragment(fragment: Fragment, frameId: Int) {
|
||||
fragmentManager?.inTransaction { replace(frameId, fragment) }
|
||||
}
|
||||
|
||||
fun androidx.fragment.app.Fragment.addFragmentToBackstack(fragment: Fragment, frameId: Int, tag: String? = null) {
|
||||
fun Fragment.addFragmentToBackstack(fragment: Fragment, frameId: Int, tag: String? = null) {
|
||||
fragmentManager?.inTransaction { replace(frameId, fragment).addToBackStack(tag) }
|
||||
}
|
||||
|
||||
fun androidx.fragment.app.Fragment.addChildFragment(fragment: Fragment, frameId: Int) {
|
||||
fun Fragment.addChildFragment(fragment: Fragment, frameId: Int) {
|
||||
childFragmentManager.inTransaction { add(frameId, fragment) }
|
||||
}
|
||||
|
||||
fun androidx.fragment.app.Fragment.replaceChildFragment(fragment: Fragment, frameId: Int) {
|
||||
fun Fragment.replaceChildFragment(fragment: Fragment, frameId: Int) {
|
||||
childFragmentManager.inTransaction { replace(frameId, fragment) }
|
||||
}
|
||||
|
||||
fun androidx.fragment.app.Fragment.addChildFragmentToBackstack(fragment: Fragment, frameId: Int, tag: String? = null) {
|
||||
fun Fragment.addChildFragmentToBackstack(fragment: Fragment, frameId: Int, tag: String? = null) {
|
||||
childFragmentManager.inTransaction { replace(frameId, fragment).addToBackStack(tag) }
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
/*
|
||||
* 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.extensions
|
||||
|
||||
import android.widget.TextView
|
||||
import androidx.core.view.isVisible
|
||||
|
||||
/**
|
||||
* Set a text in the TextView, or set visibility to GONE it if the text is null
|
||||
*/
|
||||
fun TextView.setTextOrHide(newText: String?, hideWhenBlank: Boolean = true) {
|
||||
if (newText == null
|
||||
|| (newText.isBlank() && hideWhenBlank)) {
|
||||
isVisible = false
|
||||
} else {
|
||||
this.text = newText
|
||||
isVisible = true
|
||||
}
|
||||
}
|
@ -100,7 +100,7 @@ abstract class VectorBaseFragment : BaseMvRxFragment(), OnBackPressed {
|
||||
|
||||
override fun invalidate() {
|
||||
//no-ops by default
|
||||
// TODO Remove default implementation?
|
||||
Timber.w("invalidate() method has not been implemented")
|
||||
}
|
||||
|
||||
protected fun setArguments(args: Parcelable? = null) {
|
||||
|
@ -0,0 +1,38 @@
|
||||
/*
|
||||
* 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.resources
|
||||
|
||||
import android.content.res.Resources
|
||||
import androidx.annotation.ArrayRes
|
||||
import androidx.annotation.NonNull
|
||||
|
||||
class StringArrayProvider(private val resources: Resources) {
|
||||
|
||||
/**
|
||||
* Returns a localized string array from the application's package's
|
||||
* default string array table.
|
||||
*
|
||||
* @param resId Resource id for the string array
|
||||
* @return The string array associated with the resource, stripped of styled
|
||||
* text information.
|
||||
*/
|
||||
@NonNull
|
||||
fun getStringArray(@ArrayRes resId: Int): Array<String> {
|
||||
return resources.getStringArray(resId)
|
||||
}
|
||||
|
||||
}
|
@ -29,10 +29,9 @@ 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>() {
|
||||
@EpoxyModelClass(layout = R.layout.item_public_room)
|
||||
abstract class PublicRoomItem : VectorEpoxyModel<PublicRoomItem.Holder>() {
|
||||
|
||||
// TODO Manage join state waiting from the sync
|
||||
enum class JoinState {
|
||||
NOT_JOINED,
|
||||
JOINING,
|
||||
@ -82,15 +81,15 @@ abstract class RoomDirectoryItem : VectorEpoxyModel<RoomDirectoryItem.Holder>()
|
||||
|
||||
|
||||
class Holder : VectorEpoxyHolder() {
|
||||
val rootView by bind<ViewGroup>(R.id.itemRoomDirectoryLayout)
|
||||
val rootView by bind<ViewGroup>(R.id.itemPublicRoomLayout)
|
||||
|
||||
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 avatarView by bind<ImageView>(R.id.itemPublicRoomAvatar)
|
||||
val nameView by bind<TextView>(R.id.itemPublicRoomName)
|
||||
val counterView by bind<TextView>(R.id.itemPublicRoomMembersCount)
|
||||
|
||||
val joinedView by bind<View>(R.id.itemRoomDirectoryJoined)
|
||||
val joinButton by bind<View>(R.id.itemRoomDirectoryJoin)
|
||||
val joiningView by bind<View>(R.id.itemRoomDirectoryJoining)
|
||||
val joinedView by bind<View>(R.id.itemPublicRoomJoined)
|
||||
val joinButton by bind<View>(R.id.itemPublicRoomJoin)
|
||||
val joiningView by bind<View>(R.id.itemPublicRoomJoining)
|
||||
}
|
||||
|
||||
}
|
@ -28,11 +28,11 @@ 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>() {
|
||||
class PublicRoomsController(private val stringProvider: StringProvider) : TypedEpoxyController<PublicRoomsViewState>() {
|
||||
|
||||
var callback: Callback? = null
|
||||
|
||||
override fun buildModels(viewState: RoomDirectoryViewState) {
|
||||
override fun buildModels(viewState: PublicRoomsViewState) {
|
||||
val publicRooms = viewState.publicRooms
|
||||
|
||||
if (publicRooms.isEmpty()
|
||||
@ -74,17 +74,17 @@ class RoomDirectoryController(private val stringProvider: StringProvider) : Type
|
||||
}
|
||||
}
|
||||
|
||||
private fun buildPublicRoom(publicRoom: PublicRoom, viewState: RoomDirectoryViewState) {
|
||||
roomDirectoryItem {
|
||||
private fun buildPublicRoom(publicRoom: PublicRoom, viewState: PublicRoomsViewState) {
|
||||
publicRoomItem {
|
||||
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)
|
||||
viewState.joinedRoomsIds.contains(publicRoom.roomId) -> joinState(PublicRoomItem.JoinState.JOINED)
|
||||
viewState.joiningRoomsIds.contains(publicRoom.roomId) -> joinState(PublicRoomItem.JoinState.JOINING)
|
||||
else -> joinState(PublicRoomItem.JoinState.NOT_JOINED)
|
||||
}
|
||||
joinListener {
|
||||
callback?.onPublicRoomJoin(publicRoom)
|
@ -21,13 +21,15 @@ 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.activityViewModel
|
||||
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.extensions.addFragmentToBackstack
|
||||
import im.vector.riotredesign.core.platform.SimpleTextWatcher
|
||||
import im.vector.riotredesign.core.platform.VectorBaseFragment
|
||||
import kotlinx.android.synthetic.main.fragment_room_directory.*
|
||||
import im.vector.riotredesign.features.roomdirectory.picker.RoomDirectoryPickerFragment
|
||||
import kotlinx.android.synthetic.main.fragment_public_rooms.*
|
||||
import org.koin.android.ext.android.get
|
||||
import timber.log.Timber
|
||||
|
||||
@ -38,19 +40,21 @@ import timber.log.Timber
|
||||
*
|
||||
* FIXME Rotate screen launch again the request
|
||||
*
|
||||
* For Nad:
|
||||
* TODO For Nad:
|
||||
* Display number of rooms?
|
||||
* Picto size are not correct
|
||||
* Where I put the room directory picker?
|
||||
* World Readable badge
|
||||
* Guest can join badge
|
||||
*
|
||||
*/
|
||||
class RoomDirectoryFragment : VectorBaseFragment(), RoomDirectoryController.Callback {
|
||||
class PublicRoomsFragment : VectorBaseFragment(), PublicRoomsController.Callback {
|
||||
|
||||
private val viewModel: RoomDirectoryViewModel by fragmentViewModel()
|
||||
private val viewModel: RoomDirectoryViewModel by activityViewModel()
|
||||
|
||||
private val roomDirectoryController = RoomDirectoryController(get())
|
||||
private val publicRoomsController = PublicRoomsController(get())
|
||||
|
||||
override fun getLayoutResId() = R.layout.fragment_room_directory
|
||||
override fun getLayoutResId() = R.layout.fragment_public_rooms
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
@ -62,16 +66,20 @@ class RoomDirectoryFragment : VectorBaseFragment(), RoomDirectoryController.Call
|
||||
it.setDisplayHomeAsUpEnabled(true)
|
||||
}
|
||||
|
||||
roomDirectoryFilter.addTextChangedListener(object : SimpleTextWatcher() {
|
||||
publicRoomsFilter.addTextChangedListener(object : SimpleTextWatcher() {
|
||||
override fun afterTextChanged(s: Editable) {
|
||||
// TODO Debounce
|
||||
viewModel.filterWith(roomDirectoryFilter.text.toString())
|
||||
viewModel.filterWith(publicRoomsFilter.text.toString())
|
||||
}
|
||||
})
|
||||
|
||||
createNewRoom.setOnClickListener {
|
||||
publicRoomsCreateNewRoom.setOnClickListener {
|
||||
vectorBaseActivity.notImplemented()
|
||||
}
|
||||
|
||||
publicRoomsChangeDirectory.setOnClickListener {
|
||||
vectorBaseActivity.addFragmentToBackstack(RoomDirectoryPickerFragment(), R.id.simpleFragmentContainer)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
||||
@ -82,14 +90,14 @@ class RoomDirectoryFragment : VectorBaseFragment(), RoomDirectoryController.Call
|
||||
|
||||
private fun setupRecyclerView() {
|
||||
val epoxyVisibilityTracker = EpoxyVisibilityTracker()
|
||||
epoxyVisibilityTracker.attach(roomDirectoryList)
|
||||
epoxyVisibilityTracker.attach(publicRoomsList)
|
||||
|
||||
val layoutManager = LinearLayoutManager(context)
|
||||
|
||||
roomDirectoryList.layoutManager = layoutManager
|
||||
roomDirectoryController.callback = this
|
||||
publicRoomsList.layoutManager = layoutManager
|
||||
publicRoomsController.callback = this
|
||||
|
||||
roomDirectoryList.setController(roomDirectoryController)
|
||||
publicRoomsList.setController(publicRoomsController)
|
||||
}
|
||||
|
||||
override fun onPublicRoomClicked(publicRoom: PublicRoom) {
|
||||
@ -108,6 +116,9 @@ class RoomDirectoryFragment : VectorBaseFragment(), RoomDirectoryController.Call
|
||||
|
||||
override fun invalidate() = withState(viewModel) { state ->
|
||||
// Populate list with Epoxy
|
||||
roomDirectoryController.setData(state)
|
||||
publicRoomsController.setData(state)
|
||||
|
||||
// Directory name
|
||||
publicRoomsDirectoryName.text = state.roomDirectoryDisplayName
|
||||
}
|
||||
}
|
@ -24,6 +24,7 @@ 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.session.room.model.thirdparty.RoomDirectoryData
|
||||
import im.vector.matrix.android.api.util.Cancelable
|
||||
import im.vector.matrix.rx.rx
|
||||
import im.vector.riotredesign.core.platform.VectorViewModel
|
||||
@ -32,13 +33,13 @@ import timber.log.Timber
|
||||
|
||||
private const val PUBLIC_ROOMS_LIMIT = 20
|
||||
|
||||
class RoomDirectoryViewModel(initialState: RoomDirectoryViewState,
|
||||
private val session: Session) : VectorViewModel<RoomDirectoryViewState>(initialState) {
|
||||
class RoomDirectoryViewModel(initialState: PublicRoomsViewState,
|
||||
private val session: Session) : VectorViewModel<PublicRoomsViewState>(initialState) {
|
||||
|
||||
companion object : MvRxViewModelFactory<RoomDirectoryViewModel, RoomDirectoryViewState> {
|
||||
companion object : MvRxViewModelFactory<RoomDirectoryViewModel, PublicRoomsViewState> {
|
||||
|
||||
@JvmStatic
|
||||
override fun create(viewModelContext: ViewModelContext, state: RoomDirectoryViewState): RoomDirectoryViewModel? {
|
||||
override fun create(viewModelContext: ViewModelContext, state: PublicRoomsViewState): RoomDirectoryViewModel? {
|
||||
val currentSession = viewModelContext.activity.get<Session>()
|
||||
|
||||
return RoomDirectoryViewModel(state, currentSession)
|
||||
@ -52,10 +53,19 @@ class RoomDirectoryViewModel(initialState: RoomDirectoryViewState,
|
||||
|
||||
private var currentTask: Cancelable? = null
|
||||
|
||||
// Default RoomDirectoryData
|
||||
private var roomDirectoryData = RoomDirectoryData()
|
||||
|
||||
init {
|
||||
// Load with empty filter
|
||||
load()
|
||||
|
||||
setState {
|
||||
copy(
|
||||
roomDirectoryDisplayName = roomDirectoryData.displayName
|
||||
)
|
||||
}
|
||||
|
||||
// Observe joined room (from the sync)
|
||||
observeJoinedRooms()
|
||||
}
|
||||
@ -84,11 +94,33 @@ class RoomDirectoryViewModel(initialState: RoomDirectoryViewState,
|
||||
}
|
||||
}
|
||||
|
||||
fun setRoomDirectoryData(roomDirectoryData: RoomDirectoryData) {
|
||||
if (this.roomDirectoryData == roomDirectoryData) {
|
||||
return
|
||||
}
|
||||
|
||||
this.roomDirectoryData = roomDirectoryData
|
||||
|
||||
reset()
|
||||
|
||||
load()
|
||||
}
|
||||
|
||||
fun filterWith(filter: String) {
|
||||
if (currentFilter == filter) {
|
||||
return
|
||||
}
|
||||
|
||||
currentTask?.cancel()
|
||||
|
||||
currentFilter = filter
|
||||
|
||||
reset()
|
||||
|
||||
load()
|
||||
}
|
||||
|
||||
private fun reset() {
|
||||
// Reset since token
|
||||
since = null
|
||||
|
||||
@ -96,10 +128,10 @@ class RoomDirectoryViewModel(initialState: RoomDirectoryViewState,
|
||||
copy(
|
||||
publicRooms = emptyList(),
|
||||
asyncPublicRoomsRequest = Loading(),
|
||||
hasMore = false)
|
||||
hasMore = false,
|
||||
roomDirectoryDisplayName = roomDirectoryData.displayName
|
||||
)
|
||||
}
|
||||
|
||||
load()
|
||||
}
|
||||
|
||||
fun loadMore() {
|
||||
@ -115,13 +147,13 @@ class RoomDirectoryViewModel(initialState: RoomDirectoryViewState,
|
||||
}
|
||||
|
||||
private fun load() {
|
||||
currentTask = session.getPublicRooms(null, // TODO session.sessionParams.homeServerConnectionConfig.homeServerUri.toString(),
|
||||
currentTask = session.getPublicRooms(roomDirectoryData.homeServer,
|
||||
PublicRoomsParams(
|
||||
limit = PUBLIC_ROOMS_LIMIT,
|
||||
filter = PublicRoomsFilter(searchTerm = currentFilter),
|
||||
includeAllNetworks = false, // TODO
|
||||
includeAllNetworks = roomDirectoryData.includeAllNetworks,
|
||||
since = since,
|
||||
thirdPartyInstanceId = null // TODO
|
||||
thirdPartyInstanceId = roomDirectoryData.thirdPartyInstanceId
|
||||
),
|
||||
object : MatrixCallback<PublicRoomsResponse> {
|
||||
override fun onSuccess(data: PublicRoomsResponse) {
|
@ -21,7 +21,7 @@ import com.airbnb.mvrx.MvRxState
|
||||
import com.airbnb.mvrx.Uninitialized
|
||||
import im.vector.matrix.android.api.session.room.model.roomdirectory.PublicRoom
|
||||
|
||||
data class RoomDirectoryViewState(
|
||||
data class PublicRoomsViewState(
|
||||
// Store cumul of pagination result
|
||||
val publicRooms: List<PublicRoom> = emptyList(),
|
||||
// Current pagination request
|
||||
@ -31,5 +31,6 @@ data class RoomDirectoryViewState(
|
||||
// List of roomIds that the user wants to join
|
||||
val joiningRoomsIds: List<String> = emptyList(),
|
||||
// List of joined roomId,
|
||||
val joinedRoomsIds: List<String> = emptyList()
|
||||
val joinedRoomsIds: List<String> = emptyList(),
|
||||
val roomDirectoryDisplayName: String? = null
|
||||
) : MvRxState
|
@ -27,7 +27,7 @@ class RoomDirectoryActivity : VectorBaseActivity() {
|
||||
|
||||
override fun initUiAndData() {
|
||||
if (isFirstCreation()) {
|
||||
addFragment(RoomDirectoryFragment(), R.id.simpleFragmentContainer)
|
||||
addFragment(PublicRoomsFragment(), R.id.simpleFragmentContainer)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,75 @@
|
||||
/*
|
||||
* 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.picker
|
||||
|
||||
import android.view.ViewGroup
|
||||
import android.widget.ImageView
|
||||
import android.widget.TextView
|
||||
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.core.extensions.setTextOrHide
|
||||
import im.vector.riotredesign.core.glide.GlideApp
|
||||
|
||||
@EpoxyModelClass(layout = R.layout.item_room_directory)
|
||||
abstract class RoomDirectoryItem : VectorEpoxyModel<RoomDirectoryItem.Holder>() {
|
||||
|
||||
@EpoxyAttribute
|
||||
var directoryAvatarUrl: String? = null
|
||||
|
||||
@EpoxyAttribute
|
||||
var directoryName: String? = null
|
||||
|
||||
@EpoxyAttribute
|
||||
var directoryDescription: String? = null
|
||||
|
||||
@EpoxyAttribute
|
||||
var includeAllNetworks: Boolean = false
|
||||
|
||||
@EpoxyAttribute
|
||||
var globalListener: (() -> Unit)? = null
|
||||
|
||||
override fun bind(holder: Holder) {
|
||||
holder.rootView.setOnClickListener { globalListener?.invoke() }
|
||||
|
||||
// Avatar
|
||||
GlideApp.with(holder.avatarView)
|
||||
.load(directoryAvatarUrl)
|
||||
.apply {
|
||||
if (!includeAllNetworks) {
|
||||
placeholder(R.drawable.network_matrix)
|
||||
}
|
||||
}
|
||||
.into(holder.avatarView)
|
||||
|
||||
holder.nameView.text = directoryName
|
||||
holder.descritionView.setTextOrHide(directoryDescription)
|
||||
}
|
||||
|
||||
|
||||
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 descritionView by bind<TextView>(R.id.itemRoomDirectoryDescription)
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,139 @@
|
||||
/*
|
||||
* 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.picker
|
||||
|
||||
import com.airbnb.epoxy.TypedEpoxyController
|
||||
import com.airbnb.mvrx.Fail
|
||||
import com.airbnb.mvrx.Incomplete
|
||||
import com.airbnb.mvrx.Success
|
||||
import im.vector.matrix.android.api.auth.data.Credentials
|
||||
import im.vector.matrix.android.api.session.room.model.thirdparty.RoomDirectoryData
|
||||
import im.vector.matrix.android.api.session.room.model.thirdparty.ThirdPartyProtocol
|
||||
import im.vector.riotredesign.R
|
||||
import im.vector.riotredesign.core.epoxy.errorWithRetryItem
|
||||
import im.vector.riotredesign.core.epoxy.loadingItem
|
||||
import im.vector.riotredesign.core.resources.StringArrayProvider
|
||||
import im.vector.riotredesign.core.resources.StringProvider
|
||||
|
||||
class RoomDirectoryPickerController(private val stringProvider: StringProvider,
|
||||
private val stringArrayProvider: StringArrayProvider,
|
||||
private val credentials: Credentials
|
||||
) : TypedEpoxyController<RoomDirectoryPickerViewState>() {
|
||||
|
||||
var callback: Callback? = null
|
||||
|
||||
var index = 0
|
||||
|
||||
override fun buildModels(viewState: RoomDirectoryPickerViewState) {
|
||||
val asyncThirdPartyProtocol = viewState.asyncThirdPartyRequest
|
||||
|
||||
when (asyncThirdPartyProtocol) {
|
||||
is Success -> {
|
||||
val directories = computeDirectories(asyncThirdPartyProtocol.invoke())
|
||||
|
||||
directories.forEach {
|
||||
buildDirectory(it)
|
||||
}
|
||||
}
|
||||
is Incomplete -> {
|
||||
loadingItem {
|
||||
id("loading")
|
||||
}
|
||||
}
|
||||
is Fail -> {
|
||||
errorWithRetryItem {
|
||||
id("error")
|
||||
text(asyncThirdPartyProtocol.error.localizedMessage)
|
||||
listener { callback?.retry() }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun computeDirectories(thirdPartyProtocolData: Map<String, ThirdPartyProtocol>): List<RoomDirectoryData> {
|
||||
val result = ArrayList<RoomDirectoryData>()
|
||||
|
||||
// Add user homeserver name
|
||||
val userHsName = credentials.userId.substring(credentials.userId.indexOf(":") + 1)
|
||||
|
||||
result.add(RoomDirectoryData(
|
||||
displayName = userHsName,
|
||||
includeAllNetworks = true
|
||||
))
|
||||
|
||||
// Add user's HS but for Matrix public rooms only
|
||||
result.add(RoomDirectoryData())
|
||||
|
||||
// Add custom directory servers
|
||||
val hsNamesList = stringArrayProvider.getStringArray(R.array.room_directory_servers)
|
||||
hsNamesList.forEach {
|
||||
if (it != userHsName) {
|
||||
// Use the server name as a default display name
|
||||
result.add(RoomDirectoryData(
|
||||
displayName = it,
|
||||
includeAllNetworks = true
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
// Add result of the request
|
||||
thirdPartyProtocolData.forEach {
|
||||
it.value.instances?.forEach { thirdPartyProtocolInstance ->
|
||||
result.add(RoomDirectoryData(
|
||||
homeServer = null,
|
||||
displayName = thirdPartyProtocolInstance.desc ?: "",
|
||||
thirdPartyInstanceId = thirdPartyProtocolInstance.instanceId,
|
||||
includeAllNetworks = false,
|
||||
// Default to protocol icon
|
||||
avatarUrl = thirdPartyProtocolInstance.icon ?: it.value.icon
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
private fun buildDirectory(roomDirectoryData: RoomDirectoryData) {
|
||||
|
||||
// TODO
|
||||
roomDirectoryItem {
|
||||
id(index++)
|
||||
|
||||
directoryName(roomDirectoryData.displayName)
|
||||
|
||||
val description = when {
|
||||
roomDirectoryData.includeAllNetworks -> stringProvider.getString(R.string.directory_server_all_rooms_on_server, roomDirectoryData.displayName)
|
||||
"Matrix" == roomDirectoryData.displayName -> stringProvider.getString(R.string.directory_server_native_rooms, roomDirectoryData.displayName)
|
||||
else -> null
|
||||
}
|
||||
|
||||
directoryDescription(description)
|
||||
directoryAvatarUrl(roomDirectoryData.avatarUrl)
|
||||
includeAllNetworks(roomDirectoryData.includeAllNetworks)
|
||||
|
||||
globalListener {
|
||||
callback?.onRoomDirectoryClicked(roomDirectoryData)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interface Callback {
|
||||
fun onRoomDirectoryClicked(roomDirectory: RoomDirectoryData)
|
||||
fun retry()
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,103 @@
|
||||
/*
|
||||
* 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.picker
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.airbnb.mvrx.activityViewModel
|
||||
import com.airbnb.mvrx.fragmentViewModel
|
||||
import com.airbnb.mvrx.withState
|
||||
import im.vector.matrix.android.api.session.Session
|
||||
import im.vector.matrix.android.api.session.room.model.thirdparty.RoomDirectoryData
|
||||
import im.vector.riotredesign.R
|
||||
import im.vector.riotredesign.core.platform.VectorBaseFragment
|
||||
import im.vector.riotredesign.features.roomdirectory.RoomDirectoryViewModel
|
||||
import kotlinx.android.synthetic.main.fragment_public_rooms.toolbar
|
||||
import kotlinx.android.synthetic.main.fragment_room_directory_picker.*
|
||||
import org.koin.android.ext.android.get
|
||||
import timber.log.Timber
|
||||
|
||||
// TODO Set title to R.string.select_room_directory
|
||||
// TODO Menu to add custom room directory (not done in RiotWeb so far...)
|
||||
class RoomDirectoryPickerFragment : VectorBaseFragment(), RoomDirectoryPickerController.Callback {
|
||||
private val viewModel: RoomDirectoryViewModel by activityViewModel()
|
||||
|
||||
private val pickerViewModel: RoomDirectoryPickerViewModel by fragmentViewModel()
|
||||
|
||||
private val roomDirectoryPickerController = RoomDirectoryPickerController(get(), get(), get<Session>().sessionParams.credentials)
|
||||
|
||||
override fun getLayoutResId() = R.layout.fragment_room_directory_picker
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
vectorBaseActivity.setSupportActionBar(toolbar)
|
||||
|
||||
vectorBaseActivity.supportActionBar?.let {
|
||||
it.setDisplayShowHomeEnabled(true)
|
||||
it.setDisplayHomeAsUpEnabled(true)
|
||||
}
|
||||
}
|
||||
|
||||
override fun getMenuRes() = R.menu.menu_directory_server_picker
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
if (item.itemId == R.id.action_add_custom_hs) {
|
||||
// TODO
|
||||
vectorBaseActivity.notImplemented()
|
||||
return true
|
||||
}
|
||||
|
||||
return super.onOptionsItemSelected(item)
|
||||
}
|
||||
|
||||
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
||||
super.onActivityCreated(savedInstanceState)
|
||||
|
||||
setupRecyclerView()
|
||||
}
|
||||
|
||||
private fun setupRecyclerView() {
|
||||
val layoutManager = LinearLayoutManager(context)
|
||||
|
||||
roomDirectoryPickerList.layoutManager = layoutManager
|
||||
roomDirectoryPickerController.callback = this
|
||||
|
||||
roomDirectoryPickerList.setController(roomDirectoryPickerController)
|
||||
}
|
||||
|
||||
|
||||
override fun onRoomDirectoryClicked(roomDirectoryData: RoomDirectoryData) {
|
||||
Timber.v("onRoomDirectoryClicked: $roomDirectoryData")
|
||||
viewModel.setRoomDirectoryData(roomDirectoryData)
|
||||
|
||||
// TODO Not the bast way to manage Fragment Backstack...
|
||||
vectorBaseActivity.onBackPressed()
|
||||
}
|
||||
|
||||
override fun retry() {
|
||||
Timber.v("Retry")
|
||||
pickerViewModel.load()
|
||||
}
|
||||
|
||||
override fun invalidate() = withState(pickerViewModel) { state ->
|
||||
// Populate list with Epoxy
|
||||
roomDirectoryPickerController.setData(state)
|
||||
}
|
||||
}
|
@ -0,0 +1,62 @@
|
||||
/*
|
||||
* 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.picker
|
||||
|
||||
import com.airbnb.mvrx.Fail
|
||||
import com.airbnb.mvrx.MvRxViewModelFactory
|
||||
import com.airbnb.mvrx.Success
|
||||
import com.airbnb.mvrx.ViewModelContext
|
||||
import im.vector.matrix.android.api.MatrixCallback
|
||||
import im.vector.matrix.android.api.session.Session
|
||||
import im.vector.matrix.android.api.session.room.model.thirdparty.ThirdPartyProtocol
|
||||
import im.vector.riotredesign.core.platform.VectorViewModel
|
||||
import org.koin.android.ext.android.get
|
||||
|
||||
class RoomDirectoryPickerViewModel(initialState: RoomDirectoryPickerViewState,
|
||||
private val session: Session) : VectorViewModel<RoomDirectoryPickerViewState>(initialState) {
|
||||
|
||||
companion object : MvRxViewModelFactory<RoomDirectoryPickerViewModel, RoomDirectoryPickerViewState> {
|
||||
|
||||
@JvmStatic
|
||||
override fun create(viewModelContext: ViewModelContext, state: RoomDirectoryPickerViewState): RoomDirectoryPickerViewModel? {
|
||||
val currentSession = viewModelContext.activity.get<Session>()
|
||||
|
||||
return RoomDirectoryPickerViewModel(state, currentSession)
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
load()
|
||||
}
|
||||
|
||||
fun load() {
|
||||
session.getThirdPartyProtocol(object : MatrixCallback<Map<String, ThirdPartyProtocol>> {
|
||||
override fun onSuccess(data: Map<String, ThirdPartyProtocol>) {
|
||||
setState {
|
||||
copy(asyncThirdPartyRequest = Success(data))
|
||||
}
|
||||
}
|
||||
|
||||
override fun onFailure(failure: Throwable) {
|
||||
setState {
|
||||
copy(asyncThirdPartyRequest = Fail(failure))
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
/*
|
||||
* 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.picker
|
||||
|
||||
import com.airbnb.mvrx.Async
|
||||
import com.airbnb.mvrx.MvRxState
|
||||
import com.airbnb.mvrx.Uninitialized
|
||||
import im.vector.matrix.android.api.session.room.model.thirdparty.ThirdPartyProtocol
|
||||
|
||||
data class RoomDirectoryPickerViewState(
|
||||
val asyncThirdPartyRequest: Async<Map<String, ThirdPartyProtocol>> = Uninitialized
|
||||
) : MvRxState
|
@ -8,11 +8,11 @@
|
||||
android:background="@color/pale_grey">
|
||||
|
||||
<com.airbnb.epoxy.EpoxyRecyclerView
|
||||
android:id="@+id/roomDirectoryList"
|
||||
android:id="@+id/publicRoomsList"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_behavior="@string/appbar_scrolling_view_behavior"
|
||||
tools:listitem="@layout/item_room_directory" />
|
||||
tools:listitem="@layout/item_public_room" />
|
||||
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
android:layout_width="match_parent"
|
||||
@ -35,7 +35,7 @@
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<EditText
|
||||
android:id="@+id/roomDirectoryFilter"
|
||||
android:id="@+id/publicRoomsFilter"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="@dimen/layout_horizontal_margin"
|
||||
@ -45,7 +45,7 @@
|
||||
</androidx.appcompat.widget.Toolbar>
|
||||
|
||||
<Button
|
||||
android:id="@+id/createNewRoom"
|
||||
android:id="@+id/publicRoomsCreateNewRoom"
|
||||
style="@style/VectorButtonStyleFlat"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="48dp"
|
||||
@ -56,6 +56,26 @@
|
||||
android:drawablePadding="13dp"
|
||||
android:text="@string/create_new_room" />
|
||||
|
||||
<!-- TODO Layout -->
|
||||
<Button
|
||||
android:id="@+id/publicRoomsChangeDirectory"
|
||||
style="@style/VectorButtonStyleFlat"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="48dp"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginLeft="16dp"
|
||||
android:text="RoomDirectory" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/publicRoomsDirectoryName"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginLeft="16dp"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:textColor="#FFFFFF"
|
||||
tools:text="RoomDirectoryName" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</com.google.android.material.appbar.CollapsingToolbarLayout>
|
@ -0,0 +1,35 @@
|
||||
<?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">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
|
||||
<androidx.appcompat.widget.Toolbar
|
||||
android:id="@+id/toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<com.airbnb.epoxy.EpoxyRecyclerView
|
||||
android:id="@+id/roomDirectoryPickerList"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/toolbar"
|
||||
tools:listitem="@layout/item_room_directory" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
105
vector/src/main/res/layout/item_public_room.xml
Normal file
105
vector/src/main/res/layout/item_public_room.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/itemPublicRoomLayout"
|
||||
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/itemPublicRoomAvatar"
|
||||
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/itemPublicRoomBottomSeparator"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:src="@tools:sample/avatars" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/itemPublicRoomName"
|
||||
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/itemPublicRoomBottomSeparator"
|
||||
app:layout_constraintEnd_toStartOf="@id/itemPublicRoomMembersCount"
|
||||
app:layout_constraintStart_toEndOf="@id/itemPublicRoomAvatar"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:text="@tools:sample/lorem/random" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/itemPublicRoomMembersCount"
|
||||
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/itemPublicRoomBottomSeparator"
|
||||
app:layout_constraintEnd_toStartOf="@id/itemPublicRoomJoin"
|
||||
app:layout_constraintStart_toEndOf="@id/itemPublicRoomName"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:text="148" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/itemPublicRoomJoin"
|
||||
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/itemPublicRoomBottomSeparator"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/itemPublicRoomJoined"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:scaleType="center"
|
||||
android:src="@drawable/ic_tick"
|
||||
app:layout_constraintBottom_toTopOf="@+id/itemPublicRoomBottomSeparator"
|
||||
app:layout_constraintEnd_toEndOf="@+id/itemPublicRoomJoin"
|
||||
app:layout_constraintStart_toStartOf="@+id/itemPublicRoomJoin"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/itemPublicRoomJoining"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="24dp"
|
||||
android:scaleType="center"
|
||||
app:layout_constraintBottom_toTopOf="@+id/itemPublicRoomBottomSeparator"
|
||||
app:layout_constraintEnd_toEndOf="@+id/itemPublicRoomJoin"
|
||||
app:layout_constraintStart_toStartOf="@+id/itemPublicRoomJoin"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<View
|
||||
android:id="@+id/itemPublicRoomBottomSeparator"
|
||||
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>
|
@ -31,67 +31,34 @@
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:ellipsize="end"
|
||||
android:lines="1"
|
||||
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_constraintBottom_toTopOf="@+id/itemRoomDirectoryDescription"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/itemRoomDirectoryAvatar"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintVertical_chainStyle="packed"
|
||||
tools:text="@tools:sample/lorem/random" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/itemRoomDirectoryMembersCount"
|
||||
android:layout_width="wrap_content"
|
||||
android:id="@+id/itemRoomDirectoryDescription"
|
||||
android:layout_width="0dp"
|
||||
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:layout_marginStart="16dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:ellipsize="end"
|
||||
android:lines="1"
|
||||
android:maxLines="2"
|
||||
android:textColor="#2E2F3E"
|
||||
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" />
|
||||
app:layout_constraintStart_toEndOf="@id/itemRoomDirectoryAvatar"
|
||||
app:layout_constraintTop_toBottomOf="@id/itemRoomDirectoryName"
|
||||
tools:text="@string/directory_server_all_rooms_on_server" />
|
||||
|
||||
<View
|
||||
android:id="@+id/itemRoomDirectoryBottomSeparator"
|
||||
|
@ -4,7 +4,7 @@
|
||||
|
||||
<item
|
||||
android:id="@+id/action_add_custom_hs"
|
||||
android:icon="@drawable/ic_add_white"
|
||||
android:icon="@drawable/ic_add_black"
|
||||
android:title="@string/action_open"
|
||||
app:showAsAction="always" />
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user