forked from GitHub-Mirror/riotX-android
Crypto: clean some code + add failure send state (but not handled yet).
This commit is contained in:
parent
f2722f4766
commit
612b13808f
@ -26,10 +26,19 @@ enum class SendState {
|
|||||||
SENDING,
|
SENDING,
|
||||||
// the event has been sent
|
// the event has been sent
|
||||||
SENT,
|
SENT,
|
||||||
SYNCED;
|
// the event has been received from server
|
||||||
|
SYNCED,
|
||||||
|
// The event failed to be sent
|
||||||
|
UNDELIVERED,
|
||||||
|
// the event failed to be sent because some unknown devices have been found while encrypting it
|
||||||
|
FAILED_UNKNOWN_DEVICES;
|
||||||
|
|
||||||
fun isSent(): Boolean {
|
fun isSent(): Boolean {
|
||||||
return this == SENT || this == SYNCED
|
return this == SENT || this == SYNCED
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun hasFailed(): Boolean {
|
||||||
|
return this == UNDELIVERED || this == FAILED_UNKNOWN_DEVICES
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -47,7 +47,7 @@ internal class DeviceListManager(private val cryptoStore: IMXCryptoStore,
|
|||||||
val status = deviceTrackingStatuses[userId]!!
|
val status = deviceTrackingStatuses[userId]!!
|
||||||
if (TRACKING_STATUS_DOWNLOAD_IN_PROGRESS == status || TRACKING_STATUS_UNREACHABLE_SERVER == status) {
|
if (TRACKING_STATUS_DOWNLOAD_IN_PROGRESS == status || TRACKING_STATUS_UNREACHABLE_SERVER == status) {
|
||||||
// if a download was in progress when we got shut down, it isn't any more.
|
// if a download was in progress when we got shut down, it isn't any more.
|
||||||
deviceTrackingStatuses.put(userId, TRACKING_STATUS_PENDING_DOWNLOAD)
|
deviceTrackingStatuses[userId] = TRACKING_STATUS_PENDING_DOWNLOAD
|
||||||
isUpdated = true
|
isUpdated = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,6 +21,8 @@ package im.vector.matrix.android.internal.crypto.algorithms.megolm
|
|||||||
import android.text.TextUtils
|
import android.text.TextUtils
|
||||||
import arrow.core.Try
|
import arrow.core.Try
|
||||||
import im.vector.matrix.android.api.auth.data.Credentials
|
import im.vector.matrix.android.api.auth.data.Credentials
|
||||||
|
import im.vector.matrix.android.api.failure.Failure
|
||||||
|
import im.vector.matrix.android.api.session.crypto.MXCryptoError
|
||||||
import im.vector.matrix.android.api.session.events.model.Content
|
import im.vector.matrix.android.api.session.events.model.Content
|
||||||
import im.vector.matrix.android.api.session.events.model.EventType
|
import im.vector.matrix.android.api.session.events.model.EventType
|
||||||
import im.vector.matrix.android.internal.crypto.DeviceListManager
|
import im.vector.matrix.android.internal.crypto.DeviceListManager
|
||||||
@ -88,7 +90,7 @@ internal class MXMegolmEncryption(
|
|||||||
keysClaimedMap["ed25519"] = olmDevice.deviceEd25519Key!!
|
keysClaimedMap["ed25519"] = olmDevice.deviceEd25519Key!!
|
||||||
|
|
||||||
olmDevice.addInboundGroupSession(sessionId!!, olmDevice.getSessionKey(sessionId)!!, roomId, olmDevice.deviceCurve25519Key!!,
|
olmDevice.addInboundGroupSession(sessionId!!, olmDevice.getSessionKey(sessionId)!!, roomId, olmDevice.deviceCurve25519Key!!,
|
||||||
ArrayList(), keysClaimedMap, false)
|
ArrayList(), keysClaimedMap, false)
|
||||||
|
|
||||||
keysBackup.maybeBackupKeys()
|
keysBackup.maybeBackupKeys()
|
||||||
|
|
||||||
@ -103,10 +105,10 @@ internal class MXMegolmEncryption(
|
|||||||
private suspend fun ensureOutboundSession(devicesInRoom: MXUsersDevicesMap<MXDeviceInfo>): Try<MXOutboundSessionInfo> {
|
private suspend fun ensureOutboundSession(devicesInRoom: MXUsersDevicesMap<MXDeviceInfo>): Try<MXOutboundSessionInfo> {
|
||||||
var session = outboundSession
|
var session = outboundSession
|
||||||
if (session == null
|
if (session == null
|
||||||
// Need to make a brand new session?
|
// Need to make a brand new session?
|
||||||
|| session.needsRotation(sessionRotationPeriodMsgs, sessionRotationPeriodMs)
|
|| session.needsRotation(sessionRotationPeriodMsgs, sessionRotationPeriodMs)
|
||||||
// Determine if we have shared with anyone we shouldn't have
|
// Determine if we have shared with anyone we shouldn't have
|
||||||
|| session.sharedWithTooManyDevices(devicesInRoom)) {
|
|| session.sharedWithTooManyDevices(devicesInRoom)) {
|
||||||
session = prepareNewSessionInRoom()
|
session = prepareNewSessionInRoom()
|
||||||
outboundSession = session
|
outboundSession = session
|
||||||
}
|
}
|
||||||
@ -192,7 +194,7 @@ internal class MXMegolmEncryption(
|
|||||||
return ensureOlmSessionsForDevicesAction.handle(devicesByUser)
|
return ensureOlmSessionsForDevicesAction.handle(devicesByUser)
|
||||||
.flatMap {
|
.flatMap {
|
||||||
Timber.v("## shareUserDevicesKey() : ensureOlmSessionsForDevices succeeds after "
|
Timber.v("## shareUserDevicesKey() : ensureOlmSessionsForDevices succeeds after "
|
||||||
+ (System.currentTimeMillis() - t0) + " ms")
|
+ (System.currentTimeMillis() - t0) + " ms")
|
||||||
val contentMap = MXUsersDevicesMap<Any>()
|
val contentMap = MXUsersDevicesMap<Any>()
|
||||||
var haveTargets = false
|
var haveTargets = false
|
||||||
val userIds = it.userIds
|
val userIds = it.userIds
|
||||||
@ -227,7 +229,7 @@ internal class MXMegolmEncryption(
|
|||||||
sendToDeviceTask.execute(sendToDeviceParams)
|
sendToDeviceTask.execute(sendToDeviceParams)
|
||||||
.map {
|
.map {
|
||||||
Timber.v("## shareUserDevicesKey() : sendToDevice succeeds after "
|
Timber.v("## shareUserDevicesKey() : sendToDevice succeeds after "
|
||||||
+ (System.currentTimeMillis() - t0) + " ms")
|
+ (System.currentTimeMillis() - t0) + " ms")
|
||||||
|
|
||||||
// Add the devices we have shared with to session.sharedWithDevices.
|
// Add the devices we have shared with to session.sharedWithDevices.
|
||||||
// we deliberately iterate over devicesByUser (ie, the devices we
|
// we deliberately iterate over devicesByUser (ie, the devices we
|
||||||
@ -291,24 +293,23 @@ internal class MXMegolmEncryption(
|
|||||||
// an m.new_device.
|
// an m.new_device.
|
||||||
return deviceListManager
|
return deviceListManager
|
||||||
.downloadKeys(userIds, false)
|
.downloadKeys(userIds, false)
|
||||||
.map {
|
.flatMap {
|
||||||
val encryptToVerifiedDevicesOnly = cryptoStore.getGlobalBlacklistUnverifiedDevices()
|
val encryptToVerifiedDevicesOnly = cryptoStore.getGlobalBlacklistUnverifiedDevices()
|
||||||
|| cryptoStore.getRoomsListBlacklistUnverifiedDevices().contains(roomId)
|
|| cryptoStore.getRoomsListBlacklistUnverifiedDevices().contains(roomId)
|
||||||
|
|
||||||
val devicesInRoom = MXUsersDevicesMap<MXDeviceInfo>()
|
val devicesInRoom = MXUsersDevicesMap<MXDeviceInfo>()
|
||||||
val unknownDevices = MXUsersDevicesMap<MXDeviceInfo>()
|
val unknownDevices = MXUsersDevicesMap<MXDeviceInfo>()
|
||||||
|
|
||||||
for (userId in it.userIds) {
|
for (userId in it.userIds) {
|
||||||
val deviceIds = it.getUserDeviceIds(userId)
|
val deviceIds = it.getUserDeviceIds(userId) ?: continue
|
||||||
|
for (deviceId in deviceIds) {
|
||||||
for (deviceId in deviceIds!!) {
|
val deviceInfo = it.getObject(deviceId, userId) ?: continue
|
||||||
val deviceInfo = it.getObject(deviceId, userId)
|
if (warnOnUnknownDevicesRepository.warnOnUnknownDevices() && deviceInfo.isUnknown) {
|
||||||
if (warnOnUnknownDevicesRepository.warnOnUnknownDevices() && deviceInfo!!.isUnknown) {
|
|
||||||
// The device is not yet known by the user
|
// The device is not yet known by the user
|
||||||
unknownDevices.setObject(deviceInfo, userId, deviceId)
|
unknownDevices.setObject(deviceInfo, userId, deviceId)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if (deviceInfo!!.isBlocked) {
|
if (deviceInfo.isBlocked) {
|
||||||
// Remove any blocked devices
|
// Remove any blocked devices
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@ -324,7 +325,13 @@ internal class MXMegolmEncryption(
|
|||||||
devicesInRoom.setObject(deviceInfo, userId, deviceId)
|
devicesInRoom.setObject(deviceInfo, userId, deviceId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
devicesInRoom
|
if (unknownDevices.isEmpty) {
|
||||||
|
Try.just(devicesInRoom)
|
||||||
|
} else {
|
||||||
|
val cryptoError = MXCryptoError(MXCryptoError.UNKNOWN_DEVICES_CODE,
|
||||||
|
MXCryptoError.UNABLE_TO_ENCRYPT, MXCryptoError.UNKNOWN_DEVICES_REASON, unknownDevices)
|
||||||
|
Try.Failure(Failure.CryptoError(cryptoError))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -175,6 +175,10 @@ public class MXUsersDevicesMap<E> implements Serializable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isEmpty(){
|
||||||
|
return mMap.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
if (null != mMap) {
|
if (null != mMap) {
|
||||||
|
@ -20,14 +20,14 @@ internal class WarnOnUnknownDeviceRepository {
|
|||||||
|
|
||||||
// TODO: set it back to true by default. Need UI
|
// TODO: set it back to true by default. Need UI
|
||||||
// Warn the user if some new devices are detected while encrypting a message.
|
// Warn the user if some new devices are detected while encrypting a message.
|
||||||
private var mWarnOnUnknownDevices = false
|
private var warnOnUnknownDevices = false
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tells if the encryption must fail if some unknown devices are detected.
|
* Tells if the encryption must fail if some unknown devices are detected.
|
||||||
*
|
*
|
||||||
* @return true to warn when some unknown devices are detected.
|
* @return true to warn when some unknown devices are detected.
|
||||||
*/
|
*/
|
||||||
fun warnOnUnknownDevices() = mWarnOnUnknownDevices
|
fun warnOnUnknownDevices() = warnOnUnknownDevices
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update the warn status when some unknown devices are detected.
|
* Update the warn status when some unknown devices are detected.
|
||||||
@ -35,7 +35,7 @@ internal class WarnOnUnknownDeviceRepository {
|
|||||||
* @param warn true to warn when some unknown devices are detected.
|
* @param warn true to warn when some unknown devices are detected.
|
||||||
*/
|
*/
|
||||||
fun setWarnOnUnknownDevices(warn: Boolean) {
|
fun setWarnOnUnknownDevices(warn: Boolean) {
|
||||||
mWarnOnUnknownDevices = warn
|
warnOnUnknownDevices = warn
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -36,6 +36,7 @@ import im.vector.matrix.android.internal.session.room.relation.DefaultUpdateQuic
|
|||||||
import im.vector.matrix.android.internal.session.room.relation.FindReactionEventForUndoTask
|
import im.vector.matrix.android.internal.session.room.relation.FindReactionEventForUndoTask
|
||||||
import im.vector.matrix.android.internal.session.room.relation.UpdateQuickReactionTask
|
import im.vector.matrix.android.internal.session.room.relation.UpdateQuickReactionTask
|
||||||
import im.vector.matrix.android.internal.session.room.send.LocalEchoEventFactory
|
import im.vector.matrix.android.internal.session.room.send.LocalEchoEventFactory
|
||||||
|
import im.vector.matrix.android.internal.session.room.send.LocalEchoUpdater
|
||||||
import im.vector.matrix.android.internal.session.room.state.DefaultSendStateTask
|
import im.vector.matrix.android.internal.session.room.state.DefaultSendStateTask
|
||||||
import im.vector.matrix.android.internal.session.room.state.SendStateTask
|
import im.vector.matrix.android.internal.session.room.state.SendStateTask
|
||||||
import im.vector.matrix.android.internal.session.room.timeline.*
|
import im.vector.matrix.android.internal.session.room.timeline.*
|
||||||
@ -76,6 +77,10 @@ class RoomModule {
|
|||||||
LocalEchoEventFactory(get(), get())
|
LocalEchoEventFactory(get(), get())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
scope(DefaultSession.SCOPE) {
|
||||||
|
LocalEchoUpdater(get())
|
||||||
|
}
|
||||||
|
|
||||||
scope(DefaultSession.SCOPE) {
|
scope(DefaultSession.SCOPE) {
|
||||||
RoomFactory(get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get())
|
RoomFactory(get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get())
|
||||||
}
|
}
|
||||||
|
@ -21,8 +21,10 @@ import androidx.work.Worker
|
|||||||
import androidx.work.WorkerParameters
|
import androidx.work.WorkerParameters
|
||||||
import com.squareup.moshi.JsonClass
|
import com.squareup.moshi.JsonClass
|
||||||
import im.vector.matrix.android.api.MatrixCallback
|
import im.vector.matrix.android.api.MatrixCallback
|
||||||
|
import im.vector.matrix.android.api.failure.Failure
|
||||||
import im.vector.matrix.android.api.session.crypto.CryptoService
|
import im.vector.matrix.android.api.session.crypto.CryptoService
|
||||||
import im.vector.matrix.android.api.session.events.model.Event
|
import im.vector.matrix.android.api.session.events.model.Event
|
||||||
|
import im.vector.matrix.android.api.session.room.send.SendState
|
||||||
import im.vector.matrix.android.internal.crypto.model.MXEncryptEventContentResult
|
import im.vector.matrix.android.internal.crypto.model.MXEncryptEventContentResult
|
||||||
import im.vector.matrix.android.internal.di.MatrixKoinComponent
|
import im.vector.matrix.android.internal.di.MatrixKoinComponent
|
||||||
import im.vector.matrix.android.internal.util.WorkerParamsFactory
|
import im.vector.matrix.android.internal.util.WorkerParamsFactory
|
||||||
@ -40,16 +42,18 @@ internal class EncryptEventWorker(context: Context, params: WorkerParameters)
|
|||||||
)
|
)
|
||||||
|
|
||||||
private val crypto by inject<CryptoService>()
|
private val crypto by inject<CryptoService>()
|
||||||
|
private val localEchoUpdater by inject<LocalEchoUpdater>()
|
||||||
|
|
||||||
override fun doWork(): Result {
|
override fun doWork(): Result {
|
||||||
|
|
||||||
val params = WorkerParamsFactory.fromData<Params>(inputData)
|
val params = WorkerParamsFactory.fromData<Params>(inputData)
|
||||||
?: return Result.success()
|
?: return Result.success()
|
||||||
|
|
||||||
val localEvent = params.event
|
val localEvent = params.event
|
||||||
if (localEvent.eventId == null) {
|
if (localEvent.eventId == null) {
|
||||||
return Result.success()
|
return Result.success()
|
||||||
}
|
}
|
||||||
|
localEchoUpdater.updateSendState(localEvent.eventId, SendState.ENCRYPTING)
|
||||||
|
|
||||||
// TODO Better async handling
|
// TODO Better async handling
|
||||||
val latch = CountDownLatch(1)
|
val latch = CountDownLatch(1)
|
||||||
@ -76,19 +80,21 @@ internal class EncryptEventWorker(context: Context, params: WorkerParameters)
|
|||||||
latch.await()
|
latch.await()
|
||||||
|
|
||||||
val safeResult = result
|
val safeResult = result
|
||||||
// TODO Update local echo
|
if (safeResult != null) {
|
||||||
return if (error != null) {
|
|
||||||
Result.success() // TODO Pass error!!)
|
|
||||||
} else if (safeResult != null) {
|
|
||||||
val encryptedEvent = localEvent.copy(
|
val encryptedEvent = localEvent.copy(
|
||||||
type = safeResult.mEventType,
|
type = safeResult.mEventType,
|
||||||
content = safeResult.mEventContent
|
content = safeResult.mEventContent
|
||||||
)
|
)
|
||||||
val nextWorkerParams = SendEventWorker.Params(params.roomId, encryptedEvent)
|
val nextWorkerParams = SendEventWorker.Params(params.roomId, encryptedEvent)
|
||||||
Result.success(WorkerParamsFactory.toData(nextWorkerParams))
|
return Result.success(WorkerParamsFactory.toData(nextWorkerParams))
|
||||||
|
|
||||||
} else {
|
|
||||||
Result.success()
|
|
||||||
}
|
}
|
||||||
|
val safeError = error
|
||||||
|
val sendState = when (safeError) {
|
||||||
|
is Failure.CryptoError -> SendState.FAILED_UNKNOWN_DEVICES
|
||||||
|
else -> SendState.UNDELIVERED
|
||||||
|
}
|
||||||
|
localEchoUpdater.updateSendState(localEvent.eventId, sendState)
|
||||||
|
//always return success, or the chain will be stuck for ever!
|
||||||
|
return Result.success()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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.matrix.android.internal.session.room.send
|
||||||
|
|
||||||
|
import com.zhuinden.monarchy.Monarchy
|
||||||
|
import im.vector.matrix.android.api.session.room.send.SendState
|
||||||
|
import im.vector.matrix.android.internal.database.model.EventEntity
|
||||||
|
import im.vector.matrix.android.internal.database.query.where
|
||||||
|
import im.vector.matrix.android.internal.util.tryTransactionAsync
|
||||||
|
|
||||||
|
internal class LocalEchoUpdater(private val monarchy: Monarchy) {
|
||||||
|
|
||||||
|
fun updateSendState(eventId: String, sendState: SendState) {
|
||||||
|
monarchy.tryTransactionAsync { realm ->
|
||||||
|
val sendingEventEntity = EventEntity.where(realm, eventId).findFirst()
|
||||||
|
if (sendingEventEntity != null) {
|
||||||
|
sendingEventEntity.sendState = sendState
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -22,6 +22,7 @@ import androidx.work.WorkerParameters
|
|||||||
import com.squareup.moshi.JsonClass
|
import com.squareup.moshi.JsonClass
|
||||||
import im.vector.matrix.android.api.failure.Failure
|
import im.vector.matrix.android.api.failure.Failure
|
||||||
import im.vector.matrix.android.api.session.events.model.Event
|
import im.vector.matrix.android.api.session.events.model.Event
|
||||||
|
import im.vector.matrix.android.api.session.room.send.SendState
|
||||||
import im.vector.matrix.android.internal.di.MatrixKoinComponent
|
import im.vector.matrix.android.internal.di.MatrixKoinComponent
|
||||||
import im.vector.matrix.android.internal.network.executeRequest
|
import im.vector.matrix.android.internal.network.executeRequest
|
||||||
import im.vector.matrix.android.internal.session.room.RoomAPI
|
import im.vector.matrix.android.internal.session.room.RoomAPI
|
||||||
@ -39,6 +40,7 @@ internal class SendEventWorker(context: Context, params: WorkerParameters)
|
|||||||
)
|
)
|
||||||
|
|
||||||
private val roomAPI by inject<RoomAPI>()
|
private val roomAPI by inject<RoomAPI>()
|
||||||
|
private val localEchoUpdater by inject<LocalEchoUpdater>()
|
||||||
|
|
||||||
override fun doWork(): Result {
|
override fun doWork(): Result {
|
||||||
|
|
||||||
@ -50,6 +52,7 @@ internal class SendEventWorker(context: Context, params: WorkerParameters)
|
|||||||
return Result.success()
|
return Result.success()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
localEchoUpdater.updateSendState(event.eventId, SendState.SENDING)
|
||||||
val result = executeRequest<SendResponse> {
|
val result = executeRequest<SendResponse> {
|
||||||
apiCall = roomAPI.send(
|
apiCall = roomAPI.send(
|
||||||
event.eventId,
|
event.eventId,
|
||||||
@ -62,7 +65,7 @@ internal class SendEventWorker(context: Context, params: WorkerParameters)
|
|||||||
when (it) {
|
when (it) {
|
||||||
is Failure.NetworkConnection -> Result.retry()
|
is Failure.NetworkConnection -> Result.retry()
|
||||||
else -> {
|
else -> {
|
||||||
//TODO mark as failed to send?
|
localEchoUpdater.updateSendState(event.eventId, SendState.UNDELIVERED)
|
||||||
//always return success, or the chain will be stuck for ever!
|
//always return success, or the chain will be stuck for ever!
|
||||||
Result.success()
|
Result.success()
|
||||||
}
|
}
|
||||||
|
@ -56,7 +56,6 @@ internal object TimelineSendEventWorkCommon {
|
|||||||
WorkManager.getInstance()
|
WorkManager.getInstance()
|
||||||
.beginUniqueWork(buildWorkIdentifier(roomId), ExistingWorkPolicy.APPEND, workRequest)
|
.beginUniqueWork(buildWorkIdentifier(roomId), ExistingWorkPolicy.APPEND, workRequest)
|
||||||
.enqueue()
|
.enqueue()
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inline fun <reified W : ListenableWorker> createWork(data: Data): OneTimeWorkRequest {
|
inline fun <reified W : ListenableWorker> createWork(data: Data): OneTimeWorkRequest {
|
||||||
|
@ -56,12 +56,12 @@ abstract class AbsMessageItem<H : AbsMessageItem.Holder> : BaseEventItem<H>() {
|
|||||||
var reactionPillCallback: TimelineEventController.ReactionPillCallback? = null
|
var reactionPillCallback: TimelineEventController.ReactionPillCallback? = null
|
||||||
|
|
||||||
@EpoxyAttribute
|
@EpoxyAttribute
|
||||||
var avatarCallback: TimelineEventController.AvatarCallback?= null
|
var avatarCallback: TimelineEventController.AvatarCallback? = null
|
||||||
|
|
||||||
private val _avatarClickListener = DebouncedClickListener(View.OnClickListener {
|
private val _avatarClickListener = DebouncedClickListener(View.OnClickListener {
|
||||||
avatarCallback?.onAvatarClicked(informationData)
|
avatarCallback?.onAvatarClicked(informationData)
|
||||||
})
|
})
|
||||||
private val _memberNameClickListener = DebouncedClickListener(View.OnClickListener {
|
private val _memberNameClickListener = DebouncedClickListener(View.OnClickListener {
|
||||||
avatarCallback?.onMemberNameClicked(informationData)
|
avatarCallback?.onMemberNameClicked(informationData)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user