Merge pull request #459 from vector-im/feature/clenup_after_hol

Review of merged PRs
This commit is contained in:
Benoit Marty 2019-08-06 18:39:37 +02:00 committed by GitHub
commit d9f448c9aa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
28 changed files with 263 additions and 400 deletions

View File

@ -149,7 +149,7 @@ object MatrixPatterns {
return null
}

val index = matrixId.lastIndexOf(":")
val index = matrixId.indexOf(":")

return if (index == -1) {
null

View File

@ -82,8 +82,13 @@ data class Event(
) {


@Transient
var mxDecryptionResult: OlmDecryptionResult? = null

@Transient
var mCryptoError: MXCryptoError.ErrorType? = null

@Transient
var sendState: SendState = SendState.UNKNOWN


@ -99,42 +104,6 @@ data class Event(
// Crypto
//==============================================================================================================

// /**
// * For encrypted events, the plaintext payload for the event.
// * This is a small MXEvent instance with typically value for `type` and 'content' fields.
// */
// @Transient
// var mClearEvent: Event? = null
// private set
//
// /**
// * Curve25519 key which we believe belongs to the sender of the event.
// * See `senderKey` property.
// */
// @Transient
// private var mSenderCurve25519Key: String? = null
//
// /**
// * Ed25519 key which the sender of this event (for olm) or the creator of the megolm session (for megolm) claims to own.
// * See `claimedEd25519Key` property.
// */
// @Transient
// private var mClaimedEd25519Key: String? = null
//
// /**
// * Curve25519 keys of devices involved in telling us about the senderCurve25519Key and claimedEd25519Key.
// * See `forwardingCurve25519KeyChain` property.
// */
// @Transient
// private var mForwardingCurve25519KeyChain: List<String> = ArrayList()
//
// /**
// * Decryption error
// */
// @Transient
// var mCryptoError: MXCryptoError? = null
// private set

/**
* @return true if this event is encrypted.
*/
@ -142,51 +111,11 @@ data class Event(
return TextUtils.equals(type, EventType.ENCRYPTED)
}

/**
* Update the clear data on this event.
* This is used after decrypting an event; it should not be used by applications.
*
* @param decryptionResult the decryption result, including the plaintext and some key info.
*/
// internal fun setClearData(decryptionResult: MXEventDecryptionResult?) {
// mClearEvent = null
// if (decryptionResult != null) {
// if (decryptionResult.clearEvent != null) {
// val adapter = MoshiProvider.providesMoshi().adapter(Event::class.java)
// mClearEvent = adapter.fromJsonValue(decryptionResult.clearEvent)
//
// if (mClearEvent != null) {
// mSenderCurve25519Key = decryptionResult.senderCurve25519Key
// mClaimedEd25519Key = decryptionResult.claimedEd25519Key
// mForwardingCurve25519KeyChain = decryptionResult.forwardingCurve25519KeyChain
//
// // For encrypted events with relation, the m.relates_to is kept in clear, so we need to put it back
// // in the clear event
// try {
// content?.get("m.relates_to")?.let { clearRelates ->
// mClearEvent = mClearEvent?.copy(
// content = HashMap(mClearEvent!!.content).apply {
// this["m.relates_to"] = clearRelates
// }
// )
// }
// } catch (e: Exception) {
// Timber.e(e, "Unable to restore 'm.relates_to' the clear event")
// }
// }
//
//
// }
// }
// mCryptoError = null
// }

/**
* @return The curve25519 key that sent this event.
*/
fun getSenderKey(): String? {
return mxDecryptionResult?.senderKey
// return mClearEvent?.mSenderCurve25519Key ?: mSenderCurve25519Key
}

/**
@ -194,23 +123,13 @@ data class Event(
*/
fun getKeysClaimed(): Map<String, String> {
return mxDecryptionResult?.keysClaimed ?: HashMap()
// val res = HashMap<String, String>()
//
// val claimedEd25519Key = if (null != mClearEvent) mClearEvent!!.mClaimedEd25519Key else mClaimedEd25519Key
//
// if (null != claimedEd25519Key) {
// res["ed25519"] = claimedEd25519Key
// }
//
// return res
}
//

/**
* @return the event type
*/
fun getClearType(): String {
return mxDecryptionResult?.payload?.get("type")?.toString()
?: type//get("type")?.toString() ?: type
return mxDecryptionResult?.payload?.get("type")?.toString() ?: type
}

/**
@ -220,30 +139,8 @@ data class Event(
return mxDecryptionResult?.payload?.get("content") as? Content ?: content
}

// /**
// * @return the linked crypto error
// */
// fun getCryptoError(): MXCryptoError? {
// return mCryptoError
// }
//
// /**
// * Update the linked crypto error
// *
// * @param error the new crypto error.
// */
// fun setCryptoError(error: MXCryptoError?) {
// mCryptoError = error
// if (null != error) {
// mClearEvent = null
// }
// }


fun toContentStringWithIndent(): String {
val contentMap = this.toContent()?.toMutableMap() ?: HashMap()
contentMap.remove("mxDecryptionResult")
contentMap.remove("mCryptoError")
val contentMap = toContent()?.toMutableMap() ?: HashMap()
return JSONObject(contentMap).toString(4)
}

@ -302,31 +199,19 @@ data class Event(


fun Event.isTextMessage(): Boolean {
if (this.getClearType() == EventType.MESSAGE) {
return getClearContent()?.toModel<MessageContent>()?.let {
when (it.type) {
MessageType.MSGTYPE_TEXT,
MessageType.MSGTYPE_EMOTE,
MessageType.MSGTYPE_NOTICE -> {
true
}
else -> false
}
} ?: false
return getClearType() == EventType.MESSAGE
&& when (getClearContent()?.toModel<MessageContent>()?.type) {
MessageType.MSGTYPE_TEXT,
MessageType.MSGTYPE_EMOTE,
MessageType.MSGTYPE_NOTICE -> true
else -> false
}
return false
}

fun Event.isImageMessage(): Boolean {
if (this.getClearType() == EventType.MESSAGE) {
return getClearContent()?.toModel<MessageContent>()?.let {
when (it.type) {
MessageType.MSGTYPE_IMAGE -> {
true
}
else -> false
}
} ?: false
return getClearType() == EventType.MESSAGE
&& when (getClearContent()?.toModel<MessageContent>()?.type) {
MessageType.MSGTYPE_IMAGE -> true
else -> false
}
return false
}

View File

@ -27,7 +27,7 @@ internal interface SessionParamsStore {

fun getAll(): List<SessionParams>

fun save(sessionParams: SessionParams): Try<SessionParams>
fun save(sessionParams: SessionParams): Try<Unit>

fun delete(userId: String): Try<Unit>


View File

@ -62,7 +62,7 @@ internal class RealmSessionParamsStore @Inject constructor(private val mapper: S
return sessionParams
}

override fun save(sessionParams: SessionParams): Try<SessionParams> {
override fun save(sessionParams: SessionParams): Try<Unit> {
return Try {
val entity = mapper.map(sessionParams)
if (entity != null) {
@ -72,7 +72,6 @@ internal class RealmSessionParamsStore @Inject constructor(private val mapper: S
}
realm.close()
}
sessionParams
}
}


View File

@ -31,3 +31,11 @@ inline fun <A> TryOf<A>.onError(f: (Throwable) -> Unit): Try<A> = fix()
fun <A> Try<A>.foldToCallback(callback: MatrixCallback<A>): Unit = fold(
{ callback.onFailure(it) },
{ callback.onSuccess(it) })

/**
* Same as doOnNext for Observables
*/
inline fun <A> Try<A>.alsoDo(f: (A) -> Unit) = map {
f(it)
it
}

View File

@ -115,7 +115,7 @@ internal class DefaultRelationService @Inject constructor(private val context: C
eventId,
reason)
val redactWorkData = WorkerParamsFactory.toData(sendContentWorkerParams)
return TimelineSendEventWorkCommon.createWork<RedactEventWorker>(redactWorkData)
return TimelineSendEventWorkCommon.createWork<RedactEventWorker>(redactWorkData, true)
}

override fun editTextMessage(targetEventId: String,
@ -199,14 +199,13 @@ internal class DefaultRelationService @Inject constructor(private val context: C
// Same parameter
val params = EncryptEventWorker.Params(credentials.userId, roomId, event, keepKeys)
val sendWorkData = WorkerParamsFactory.toData(params)
return TimelineSendEventWorkCommon.createWork<EncryptEventWorker>(sendWorkData)
return TimelineSendEventWorkCommon.createWork<EncryptEventWorker>(sendWorkData, true)
}

private fun createSendEventWork(event: Event): OneTimeWorkRequest {
val sendContentWorkerParams = SendEventWorker.Params(credentials.userId, roomId, event)
val sendWorkData = WorkerParamsFactory.toData(sendContentWorkerParams)
val workRequest = TimelineSendEventWorkCommon.createWork<SendEventWorker>(sendWorkData)
return workRequest
return TimelineSendEventWorkCommon.createWork<SendEventWorker>(sendWorkData, true)
}

override fun getEventSummaryLive(eventId: String): LiveData<EventAnnotationsSummary> {

View File

@ -22,10 +22,7 @@ import com.zhuinden.monarchy.Monarchy
import im.vector.matrix.android.api.auth.data.Credentials
import im.vector.matrix.android.api.session.content.ContentAttachmentData
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.EventType
import im.vector.matrix.android.api.session.events.model.isTextMessage
import im.vector.matrix.android.api.session.events.model.toModel
import im.vector.matrix.android.api.session.events.model.*
import im.vector.matrix.android.api.session.room.model.message.MessageContent
import im.vector.matrix.android.api.session.room.model.message.MessageType
import im.vector.matrix.android.api.session.room.send.SendService
@ -41,9 +38,11 @@ import im.vector.matrix.android.internal.database.query.where
import im.vector.matrix.android.internal.session.content.UploadContentWorker
import im.vector.matrix.android.internal.session.room.timeline.TimelineSendEventWorkCommon
import im.vector.matrix.android.internal.util.CancelableWork
import im.vector.matrix.android.internal.worker.AlwaysSuccessfulWorker
import im.vector.matrix.android.internal.worker.WorkManagerUtil
import im.vector.matrix.android.internal.worker.WorkManagerUtil.matrixOneTimeWorkRequestBuilder
import im.vector.matrix.android.internal.worker.WorkerParamsFactory
import im.vector.matrix.android.internal.worker.startChain
import timber.log.Timber
import java.util.concurrent.Executors
import java.util.concurrent.TimeUnit
@ -82,11 +81,11 @@ internal class DefaultSendService @Inject constructor(private val context: Conte
return if (cryptoService.isRoomEncrypted(roomId)) {
Timber.v("Send event in encrypted room")
val encryptWork = createEncryptEventWork(event, true)
val sendWork = createSendEventWork(event)
val sendWork = createSendEventWork(event, false)
TimelineSendEventWorkCommon.postSequentialWorks(context, roomId, encryptWork, sendWork)
CancelableWork(context, encryptWork.id)
} else {
val sendWork = createSendEventWork(event)
val sendWork = createSendEventWork(event, true)
TimelineSendEventWorkCommon.postWork(context, roomId, sendWork)
CancelableWork(context, sendWork.id)
}
@ -106,7 +105,7 @@ internal class DefaultSendService @Inject constructor(private val context: Conte
}

override fun resendTextMessage(localEcho: TimelineEvent): Cancelable? {
if (localEcho.root.isTextMessage()) {
if (localEcho.root.isTextMessage() && localEcho.root.sendState.hasFailed()) {
return sendEvent(localEcho.root)
}
return null
@ -114,7 +113,8 @@ internal class DefaultSendService @Inject constructor(private val context: Conte
}

override fun resendMediaMessage(localEcho: TimelineEvent): Cancelable? {
//TODO this need a refactoring of attachement sending
if (localEcho.root.isImageMessage() && localEcho.root.sendState.hasFailed()) {
//TODO this need a refactoring of attachement sending
// val clearContent = localEcho.root.getClearContent()
// val messageContent = clearContent?.toModel<MessageContent>() ?: return null
// when (messageContent.type) {
@ -143,8 +143,9 @@ internal class DefaultSendService @Inject constructor(private val context: Conte
// }
// }
// }
return null
}
return null

}

override fun deleteFailedEcho(localEcho: TimelineEvent) {
@ -162,15 +163,16 @@ internal class DefaultSendService @Inject constructor(private val context: Conte

override fun clearSendingQueue() {
TimelineSendEventWorkCommon.cancelAllWorks(context, roomId)
WorkManager.getInstance(context).cancelUniqueWork(buildWorkIdentifier(UPLOAD_WORK))
WorkManager.getInstance(context).cancelUniqueWork(buildWorkName(UPLOAD_WORK))

matrixOneTimeWorkRequestBuilder<FakeSendWorker>()
// Replace the worker chains with a AlwaysSuccessfulWorker, to ensure the queues are well emptied
matrixOneTimeWorkRequestBuilder<AlwaysSuccessfulWorker>()
.build().let {
TimelineSendEventWorkCommon.postWork(context, roomId, it, ExistingWorkPolicy.REPLACE)

//need to clear also image sending queue
WorkManager.getInstance(context)
.beginUniqueWork(buildWorkIdentifier(UPLOAD_WORK), ExistingWorkPolicy.REPLACE, it)
.beginUniqueWork(buildWorkName(UPLOAD_WORK), ExistingWorkPolicy.REPLACE, it)
.enqueue()
}

@ -244,26 +246,26 @@ internal class DefaultSendService @Inject constructor(private val context: Conte
val isRoomEncrypted = cryptoService.isRoomEncrypted(roomId)

val uploadWork = createUploadMediaWork(localEcho, attachment, isRoomEncrypted, startChain = true)
val sendWork = createSendEventWork(localEcho)
val sendWork = createSendEventWork(localEcho, false)

if (isRoomEncrypted) {
val encryptWork = createEncryptEventWork(localEcho, false /*not start of chain, take input error*/)

val op: Operation = WorkManager.getInstance(context)
.beginUniqueWork(buildWorkIdentifier(UPLOAD_WORK), ExistingWorkPolicy.APPEND, uploadWork)
.beginUniqueWork(buildWorkName(UPLOAD_WORK), ExistingWorkPolicy.APPEND, uploadWork)
.then(encryptWork)
.then(sendWork)
.enqueue()
op.result.addListener(Runnable {
if (op.result.isCancelled) {
Timber.e("CHAINE WAS CANCELLED")
Timber.e("CHAIN WAS CANCELLED")
} else if (op.state.value is Operation.State.FAILURE) {
Timber.e("CHAINE DID FAIL")
Timber.e("CHAIN DID FAIL")
}
}, workerFutureListenerExecutor)
} else {
WorkManager.getInstance(context)
.beginUniqueWork(buildWorkIdentifier(UPLOAD_WORK), ExistingWorkPolicy.APPEND, uploadWork)
.beginUniqueWork(buildWorkName(UPLOAD_WORK), ExistingWorkPolicy.APPEND, uploadWork)
.then(sendWork)
.enqueue()
}
@ -275,11 +277,11 @@ internal class DefaultSendService @Inject constructor(private val context: Conte
localEchoEventFactory.saveLocalEcho(monarchy, event)
}

private fun buildWorkIdentifier(identifier: String): String {
private fun buildWorkName(identifier: String): String {
return "${roomId}_$identifier"
}

private fun createEncryptEventWork(event: Event, startChain: Boolean = false): OneTimeWorkRequest {
private fun createEncryptEventWork(event: Event, startChain: Boolean): OneTimeWorkRequest {
// Same parameter
val params = EncryptEventWorker.Params(credentials.userId, roomId, event)
val sendWorkData = WorkerParamsFactory.toData(params)
@ -287,20 +289,16 @@ internal class DefaultSendService @Inject constructor(private val context: Conte
return matrixOneTimeWorkRequestBuilder<EncryptEventWorker>()
.setConstraints(WorkManagerUtil.workConstraints)
.setInputData(sendWorkData)
.apply {
if (startChain) {
setInputMerger(NoMerger::class.java)
}
}
.startChain(startChain)
.setBackoffCriteria(BackoffPolicy.LINEAR, BACKOFF_DELAY, TimeUnit.MILLISECONDS)
.build()
}

private fun createSendEventWork(event: Event): OneTimeWorkRequest {
private fun createSendEventWork(event: Event, startChain: Boolean): OneTimeWorkRequest {
val sendContentWorkerParams = SendEventWorker.Params(credentials.userId, roomId, event)
val sendWorkData = WorkerParamsFactory.toData(sendContentWorkerParams)

return TimelineSendEventWorkCommon.createWork<SendEventWorker>(sendWorkData)
return TimelineSendEventWorkCommon.createWork<SendEventWorker>(sendWorkData, startChain)
}

private fun createRedactEventWork(event: Event, reason: String?): OneTimeWorkRequest {
@ -309,23 +307,19 @@ internal class DefaultSendService @Inject constructor(private val context: Conte
}
val sendContentWorkerParams = RedactEventWorker.Params(credentials.userId, redactEvent.eventId!!, roomId, event.eventId, reason)
val redactWorkData = WorkerParamsFactory.toData(sendContentWorkerParams)
return TimelineSendEventWorkCommon.createWork<RedactEventWorker>(redactWorkData)
return TimelineSendEventWorkCommon.createWork<RedactEventWorker>(redactWorkData, true)
}

private fun createUploadMediaWork(event: Event,
attachment: ContentAttachmentData,
isRoomEncrypted: Boolean,
startChain: Boolean = false): OneTimeWorkRequest {
startChain: Boolean): OneTimeWorkRequest {
val uploadMediaWorkerParams = UploadContentWorker.Params(credentials.userId, roomId, event, attachment, isRoomEncrypted)
val uploadWorkData = WorkerParamsFactory.toData(uploadMediaWorkerParams)

return matrixOneTimeWorkRequestBuilder<UploadContentWorker>()
.setConstraints(WorkManagerUtil.workConstraints)
.apply {
if (startChain) {
setInputMerger(NoMerger::class.java)
}
}
.startChain(startChain)
.setInputData(uploadWorkData)
.setBackoffCriteria(BackoffPolicy.LINEAR, BACKOFF_DELAY, TimeUnit.MILLISECONDS)
.build()

View File

@ -18,7 +18,10 @@ package im.vector.matrix.android.internal.session.room.send
import androidx.work.Data
import androidx.work.InputMerger

class NoMerger : InputMerger() {
/**
* InputMerger which takes only the first input, to ensure an appended work will only have the specified parameters
*/
internal class NoMerger : InputMerger() {
override fun merge(inputs: MutableList<Data>): Data {
return inputs.first()
}

View File

@ -19,6 +19,7 @@ import android.content.Context
import androidx.work.*
import im.vector.matrix.android.internal.worker.WorkManagerUtil
import im.vector.matrix.android.internal.worker.WorkManagerUtil.matrixOneTimeWorkRequestBuilder
import im.vector.matrix.android.internal.worker.startChain
import java.util.concurrent.TimeUnit


@ -41,7 +42,7 @@ internal object TimelineSendEventWorkCommon {
else -> {
val firstWork = workRequests.first()
var continuation = WorkManager.getInstance(context)
.beginUniqueWork(buildWorkIdentifier(roomId), ExistingWorkPolicy.APPEND, firstWork)
.beginUniqueWork(buildWorkName(roomId), ExistingWorkPolicy.APPEND, firstWork)
for (i in 1 until workRequests.size) {
val workRequest = workRequests[i]
continuation = continuation.then(workRequest)
@ -53,23 +54,24 @@ internal object TimelineSendEventWorkCommon {

fun postWork(context: Context, roomId: String, workRequest: OneTimeWorkRequest, policy: ExistingWorkPolicy = ExistingWorkPolicy.APPEND) {
WorkManager.getInstance(context)
.beginUniqueWork(buildWorkIdentifier(roomId), policy, workRequest)
.beginUniqueWork(buildWorkName(roomId), policy, workRequest)
.enqueue()
}

inline fun <reified W : ListenableWorker> createWork(data: Data): OneTimeWorkRequest {
inline fun <reified W : ListenableWorker> createWork(data: Data, startChain: Boolean): OneTimeWorkRequest {
return matrixOneTimeWorkRequestBuilder<W>()
.setConstraints(WorkManagerUtil.workConstraints)
.startChain(startChain)
.setInputData(data)
.setBackoffCriteria(BackoffPolicy.LINEAR, BACKOFF_DELAY, TimeUnit.MILLISECONDS)
.build()
}

private fun buildWorkIdentifier(roomId: String): String {
private fun buildWorkName(roomId: String): String {
return "${roomId}_$SEND_WORK"
}

fun cancelAllWorks(context: Context, roomId: String) {
WorkManager.getInstance(context).cancelUniqueWork(buildWorkIdentifier(roomId))
WorkManager.getInstance(context).cancelUniqueWork(buildWorkName(roomId))
}
}

View File

@ -36,6 +36,6 @@ internal abstract class AccountDataModule {
}

@Binds
abstract fun bindUpdateUserAccountDataTask(updateUserAccountDataTask: DefaultUpdateUserAcountDataTask): UpdateUserAccountDataTask
abstract fun bindUpdateUserAccountDataTask(updateUserAccountDataTask: DefaultUpdateUserAccountDataTask): UpdateUserAccountDataTask

}

View File

@ -41,8 +41,8 @@ internal interface UpdateUserAccountDataTask : Task<UpdateUserAccountDataTask.Pa

}

internal class DefaultUpdateUserAcountDataTask @Inject constructor(private val accountDataApi: AccountDataAPI,
private val credentials: Credentials) : UpdateUserAccountDataTask {
internal class DefaultUpdateUserAccountDataTask @Inject constructor(private val accountDataApi: AccountDataAPI,
private val credentials: Credentials) : UpdateUserAccountDataTask {

override suspend fun execute(params: UpdateUserAccountDataTask.Params) {
return executeRequest {

View File

@ -13,13 +13,13 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package im.vector.matrix.android.internal.session.room.send
package im.vector.matrix.android.internal.worker

import android.content.Context
import androidx.work.Worker
import androidx.work.WorkerParameters

internal class FakeSendWorker(context: Context, params: WorkerParameters)
internal class AlwaysSuccessfulWorker(context: Context, params: WorkerParameters)
: Worker(context, params) {

override fun doWork(): Result {

View File

@ -0,0 +1,30 @@
/*
* Copyright 2019 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package im.vector.matrix.android.internal.worker

import androidx.work.OneTimeWorkRequest
import im.vector.matrix.android.internal.session.room.send.NoMerger

/**
* If startChain parameter is true, the builder will have a inputMerger set to [NoMerger]
*/
internal fun OneTimeWorkRequest.Builder.startChain(startChain: Boolean): OneTimeWorkRequest.Builder {
if (startChain) {
setInputMerger(NoMerger::class.java)
}
return this
}

View File

@ -31,15 +31,14 @@ class ErrorFormatter @Inject constructor(val stringProvider: StringProvider) {

fun toHumanReadable(throwable: Throwable?): String {
return when (throwable) {
null -> ""
null -> null
is Failure.NetworkConnection -> stringProvider.getString(R.string.error_no_network)
is Failure.ServerError -> {
throwable.error.message.takeIf { it.isNotEmpty() }
?: throwable.error.code.takeIf { it.isNotEmpty() }
?: stringProvider.getString(R.string.unknown_error)
?: throwable.error.code.takeIf { it.isNotEmpty() }
}
else -> throwable.localizedMessage
?: stringProvider.getString(R.string.unknown_error)
}
?: stringProvider.getString(R.string.unknown_error)
}
}

View File

@ -18,25 +18,22 @@ package im.vector.riotx.core.extensions

import android.text.Editable
import android.text.InputType
import android.text.TextWatcher
import android.view.MotionEvent
import android.view.View
import android.view.inputmethod.EditorInfo
import android.widget.EditText
import androidx.annotation.DrawableRes
import im.vector.riotx.R
import im.vector.riotx.core.platform.SimpleTextWatcher

fun EditText.setupAsSearch(@DrawableRes searchIconRes: Int = R.drawable.ic_filter,
@DrawableRes clearIconRes: Int = R.drawable.ic_x_green) {

addTextChangedListener(object : TextWatcher {
override fun afterTextChanged(editable: Editable?) {
val clearIcon = if (editable?.isNotEmpty() == true) clearIconRes else 0
addTextChangedListener(object : SimpleTextWatcher() {
override fun afterTextChanged(s: Editable) {
val clearIcon = if (s.isNotEmpty()) clearIconRes else 0
setCompoundDrawablesWithIntrinsicBounds(searchIconRes, 0, clearIcon, 0)
}

override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) = Unit
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) = Unit
})

maxLines = 1

View File

@ -19,6 +19,7 @@ package im.vector.riotx.core.platform
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import im.vector.riotx.core.extensions.postLiveEvent
import im.vector.riotx.core.utils.LiveEvent
import im.vector.riotx.features.configuration.VectorConfiguration
import timber.log.Timber
@ -46,7 +47,7 @@ class ConfigurationViewModel @Inject constructor(
if (newHash != currentConfigurationValue) {
Timber.v("Configuration: recreate the Activity")
currentConfigurationValue = newHash
_activityRestarter.postValue(LiveEvent(Unit))
_activityRestarter.postLiveEvent(Unit)
}
}
}

View File

@ -19,6 +19,7 @@ package im.vector.riotx.core.platform
import android.text.Editable
import android.text.TextWatcher


/**
* TextWatcher with default no op implementation
*/

View File

@ -24,11 +24,7 @@ import android.os.Bundle
import android.view.View
import androidx.appcompat.app.AlertDialog
import androidx.lifecycle.ViewModelProviders
import com.airbnb.mvrx.Async
import com.airbnb.mvrx.Fail
import com.airbnb.mvrx.Loading
import com.airbnb.mvrx.Success
import com.airbnb.mvrx.viewModel
import com.airbnb.mvrx.*
import im.vector.matrix.android.api.session.room.failure.CreateRoomFailure
import im.vector.riotx.R
import im.vector.riotx.core.di.ScreenComponent
@ -39,7 +35,6 @@ import im.vector.riotx.core.extensions.observeEvent
import im.vector.riotx.core.platform.SimpleFragmentActivity
import im.vector.riotx.core.platform.WaitingViewData
import kotlinx.android.synthetic.main.activity.*
import timber.log.Timber
import javax.inject.Inject

class CreateDirectRoomActivity : SimpleFragmentActivity() {
@ -98,7 +93,7 @@ class CreateDirectRoomActivity : SimpleFragmentActivity() {
} else
AlertDialog.Builder(this)
.setMessage(errorFormatter.toHumanReadable(error))
.setPositiveButton(R.string.ok) { dialog, id -> dialog.cancel() }
.setPositiveButton(R.string.ok, null)
.show()
}


View File

@ -21,7 +21,9 @@ package im.vector.riotx.features.home.createdirect
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import arrow.core.Option
import com.airbnb.mvrx.*
import com.airbnb.mvrx.ActivityViewModelContext
import com.airbnb.mvrx.MvRxViewModelFactory
import com.airbnb.mvrx.ViewModelContext
import com.jakewharton.rxrelay2.BehaviorRelay
import com.squareup.inject.assisted.Assisted
import com.squareup.inject.assisted.AssistedInject
@ -33,10 +35,8 @@ import im.vector.matrix.rx.rx
import im.vector.riotx.core.extensions.postLiveEvent
import im.vector.riotx.core.platform.VectorViewModel
import im.vector.riotx.core.utils.LiveEvent
import io.reactivex.Observable
import io.reactivex.Single
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.functions.BiFunction
import java.util.concurrent.TimeUnit

private typealias KnowUsersFilter = String
@ -103,7 +103,6 @@ class CreateDirectRoomViewModel @AssistedInject constructor(@Assisted
.execute {
copy(createAndInviteState = it)
}
.disposeOnClear()
}

private fun handleRemoveSelectedUser(action: CreateDirectRoomActions.RemoveSelectedUser) = withState { state ->

View File

@ -300,8 +300,9 @@ class RoomDetailFragment :
composerLayout.collapse()
}

private fun enterSpecialMode(event: TimelineEvent, @DrawableRes
iconRes: Int, useText: Boolean) {
private fun enterSpecialMode(event: TimelineEvent,
@DrawableRes iconRes: Int,
useText: Boolean) {
commandAutocompletePolicy.enabled = false
//switch to expanded bar
composerLayout.composerRelatedMessageTitle.apply {
@ -820,106 +821,101 @@ class RoomDetailFragment :
textComposerViewModel.process(TextComposerActions.QueryUsers(query))
}

private fun handleActions(actionData: ActionsHandler.ActionData) {
when (actionData.actionId) {
MessageMenuViewModel.ACTION_ADD_REACTION -> {
val eventId = actionData.data?.toString() ?: return
startActivityForResult(EmojiReactionPickerActivity.intent(requireContext(), eventId), REACTION_SELECT_REQUEST_CODE)
private fun handleActions(action: SimpleAction) {
when (action) {
is SimpleAction.AddReaction -> {
startActivityForResult(EmojiReactionPickerActivity.intent(requireContext(), action.eventId), REACTION_SELECT_REQUEST_CODE)
}
MessageMenuViewModel.ACTION_VIEW_REACTIONS -> {
val messageInformationData = actionData.data as? MessageInformationData
?: return
ViewReactionBottomSheet.newInstance(roomDetailArgs.roomId, messageInformationData)
is SimpleAction.ViewReactions -> {
ViewReactionBottomSheet.newInstance(roomDetailArgs.roomId, action.messageInformationData)
.show(requireActivity().supportFragmentManager, "DISPLAY_REACTIONS")
}
MessageMenuViewModel.ACTION_COPY -> {
is SimpleAction.Copy -> {
//I need info about the current selected message :/
copyToClipboard(requireContext(), actionData.data?.toString() ?: "", false)
copyToClipboard(requireContext(), action.content, false)
val msg = requireContext().getString(R.string.copied_to_clipboard)
showSnackWithMessage(msg, Snackbar.LENGTH_SHORT)
}
MessageMenuViewModel.ACTION_DELETE -> {
val eventId = actionData.data?.toString() ?: return
roomDetailViewModel.process(RoomDetailActions.RedactAction(eventId, context?.getString(R.string.event_redacted_by_user_reason)))
is SimpleAction.Delete -> {
roomDetailViewModel.process(RoomDetailActions.RedactAction(action.eventId, context?.getString(R.string.event_redacted_by_user_reason)))
}
MessageMenuViewModel.ACTION_SHARE -> {
is SimpleAction.Share -> {
//TODO current data communication is too limited
//Need to now the media type
actionData.data?.toString()?.let {
//TODO bad, just POC
BigImageViewer.imageLoader().loadImage(
actionData.hashCode(),
Uri.parse(it),
object : ImageLoader.Callback {
override fun onFinish() {}

override fun onSuccess(image: File?) {
if (image != null)
shareMedia(requireContext(), image, "image/*")
}

override fun onFail(error: Exception?) {}

override fun onCacheHit(imageType: Int, image: File?) {}

override fun onCacheMiss(imageType: Int, image: File?) {}

override fun onProgress(progress: Int) {}

override fun onStart() {}
//TODO bad, just POC
BigImageViewer.imageLoader().loadImage(
action.hashCode(),
Uri.parse(action.imageUrl),
object : ImageLoader.Callback {
override fun onFinish() {}

override fun onSuccess(image: File?) {
if (image != null)
shareMedia(requireContext(), image, "image/*")
}

)
}
override fun onFail(error: Exception?) {}

override fun onCacheHit(imageType: Int, image: File?) {}

override fun onCacheMiss(imageType: Int, image: File?) {}

override fun onProgress(progress: Int) {}

override fun onStart() {}

}
)
}
MessageMenuViewModel.VIEW_SOURCE,
MessageMenuViewModel.VIEW_DECRYPTED_SOURCE -> {
is SimpleAction.ViewSource -> {
val view = LayoutInflater.from(requireContext()).inflate(R.layout.dialog_event_content, null)
view.findViewById<TextView>(R.id.event_content_text_view)?.let {
it.text = actionData.data?.toString() ?: ""
it.text = action.content
}

AlertDialog.Builder(requireActivity())
.setView(view)
.setPositiveButton(R.string.ok) { dialog, id -> dialog.cancel() }
.setPositiveButton(R.string.ok, null)
.show()
}
MessageMenuViewModel.ACTION_QUICK_REACT -> {
//eventId,ClickedOn,Add
(actionData.data as? Triple<String, String, Boolean>)?.let { (eventId, clickedOn, add) ->
roomDetailViewModel.process(RoomDetailActions.UpdateQuickReactAction(eventId, clickedOn, add))
is SimpleAction.ViewDecryptedSource -> {
val view = LayoutInflater.from(requireContext()).inflate(R.layout.dialog_event_content, null)
view.findViewById<TextView>(R.id.event_content_text_view)?.let {
it.text = action.content
}

AlertDialog.Builder(requireActivity())
.setView(view)
.setPositiveButton(R.string.ok, null)
.show()
}
MessageMenuViewModel.ACTION_EDIT -> {
val eventId = actionData.data.toString()
roomDetailViewModel.process(RoomDetailActions.EnterEditMode(eventId))
is SimpleAction.QuickReact -> {
//eventId,ClickedOn,Add
roomDetailViewModel.process(RoomDetailActions.UpdateQuickReactAction(action.eventId, action.clickedOn, action.add))
}
MessageMenuViewModel.ACTION_QUOTE -> {
val eventId = actionData.data.toString()
roomDetailViewModel.process(RoomDetailActions.EnterQuoteMode(eventId))
is SimpleAction.Edit -> {
roomDetailViewModel.process(RoomDetailActions.EnterEditMode(action.eventId))
}
MessageMenuViewModel.ACTION_REPLY -> {
val eventId = actionData.data.toString()
roomDetailViewModel.process(RoomDetailActions.EnterReplyMode(eventId))
is SimpleAction.Quote -> {
roomDetailViewModel.process(RoomDetailActions.EnterQuoteMode(action.eventId))
}
MessageMenuViewModel.ACTION_COPY_PERMALINK -> {
val eventId = actionData.data.toString()
val permalink = PermalinkFactory.createPermalink(roomDetailArgs.roomId, eventId)
is SimpleAction.Reply -> {
roomDetailViewModel.process(RoomDetailActions.EnterReplyMode(action.eventId))
}
is SimpleAction.CopyPermalink -> {
val permalink = PermalinkFactory.createPermalink(roomDetailArgs.roomId, action.eventId)
copyToClipboard(requireContext(), permalink, false)
showSnackWithMessage(requireContext().getString(R.string.copied_to_clipboard), Snackbar.LENGTH_SHORT)

}
MessageMenuViewModel.ACTION_RESEND -> {
val eventId = actionData.data.toString()
roomDetailViewModel.process(RoomDetailActions.ResendMessage(eventId))
is SimpleAction.Resend -> {
roomDetailViewModel.process(RoomDetailActions.ResendMessage(action.eventId))
}
MessageMenuViewModel.ACTION_REMOVE -> {
val eventId = actionData.data.toString()
roomDetailViewModel.process(RoomDetailActions.RemoveFailedEcho(eventId))
is SimpleAction.Remove -> {
roomDetailViewModel.process(RoomDetailActions.RemoveFailedEcho(action.eventId))
}
else -> {
Toast.makeText(context, "Action ${actionData.actionId} not implemented", Toast.LENGTH_LONG).show()
else -> {
Toast.makeText(context, "Action $action is not implemented yet", Toast.LENGTH_LONG).show()
}
}
}

View File

@ -17,6 +17,7 @@ package im.vector.riotx.features.home.room.detail.timeline.action

import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import im.vector.riotx.core.extensions.postLiveEvent
import im.vector.riotx.core.utils.LiveEvent
import javax.inject.Inject

@ -25,15 +26,10 @@ import javax.inject.Inject
*/
class ActionsHandler @Inject constructor() : ViewModel() {

data class ActionData(
val actionId: String,
val data: Any?
)
val actionCommandEvent = MutableLiveData<LiveEvent<SimpleAction>>()

val actionCommandEvent = MutableLiveData<LiveEvent<ActionData>>()

fun fireAction(actionId: String, data: Any? = null) {
actionCommandEvent.value = LiveEvent(ActionData(actionId,data))
fun fireAction(action: SimpleAction) {
actionCommandEvent.postLiveEvent(action)
}

}

View File

@ -89,7 +89,7 @@ class MessageActionsBottomSheet : VectorBaseBottomSheetDialogFragment() {
}
menuActionFragment.interactionListener = object : MessageMenuFragment.InteractionListener {
override fun didSelectMenuAction(simpleAction: SimpleAction) {
actionHandlerModel.fireAction(simpleAction.uid, simpleAction.data)
actionHandlerModel.fireAction(simpleAction)
dismiss()
}
}
@ -105,7 +105,7 @@ class MessageActionsBottomSheet : VectorBaseBottomSheetDialogFragment() {
quickReactionFragment.interactionListener = object : QuickReactionFragment.InteractionListener {

override fun didQuickReactWith(clickedOn: String, add: Boolean, eventId: String) {
actionHandlerModel.fireAction(MessageMenuViewModel.ACTION_QUICK_REACT, Triple(eventId, clickedOn, add))
actionHandlerModel.fireAction(SimpleAction.QuickReact(eventId, clickedOn, add))
dismiss()
}
}

View File

@ -15,6 +15,8 @@
*/
package im.vector.riotx.features.home.room.detail.timeline.action

import androidx.annotation.DrawableRes
import androidx.annotation.StringRes
import com.airbnb.mvrx.*
import com.squareup.inject.assisted.Assisted
import com.squareup.inject.assisted.AssistedInject
@ -36,7 +38,24 @@ import im.vector.riotx.core.utils.isSingleEmoji
import im.vector.riotx.features.home.room.detail.timeline.item.MessageInformationData


data class SimpleAction(val uid: String, val titleRes: Int, val iconResId: Int?, val data: Any? = null)
sealed class SimpleAction(@StringRes val titleRes: Int, @DrawableRes val iconResId: Int) {
data class AddReaction(val eventId: String) : SimpleAction(R.string.message_add_reaction, R.drawable.ic_add_reaction)
data class Copy(val content: String) : SimpleAction(R.string.copy, R.drawable.ic_copy)
data class Edit(val eventId: String) : SimpleAction(R.string.edit, R.drawable.ic_edit)
data class Quote(val eventId: String) : SimpleAction(R.string.quote, R.drawable.ic_quote)
data class Reply(val eventId: String) : SimpleAction(R.string.reply, R.drawable.ic_reply)
data class Share(val imageUrl: String?) : SimpleAction(R.string.share, R.drawable.ic_share)
data class Resend(val eventId: String) : SimpleAction(R.string.global_retry, R.drawable.ic_refresh_cw)
data class Remove(val eventId: String) : SimpleAction(R.string.remove, R.drawable.ic_trash)
data class Delete(val eventId: String) : SimpleAction(R.string.delete, R.drawable.ic_delete)
data class Cancel(val eventId: String) : SimpleAction(R.string.cancel, R.drawable.ic_close_round)
data class ViewSource(val content: String) : SimpleAction(R.string.view_source, R.drawable.ic_view_source)
data class ViewDecryptedSource(val content: String) : SimpleAction(R.string.view_decrypted_source, R.drawable.ic_view_source)
data class CopyPermalink(val eventId: String) : SimpleAction(R.string.permalink, R.drawable.ic_permalink)
data class Flag(val eventId: String) : SimpleAction(R.string.report_content, R.drawable.ic_flag)
data class QuickReact(val eventId: String, val clickedOn: String, val add: Boolean) : SimpleAction(0, 0)
data class ViewReactions(val messageInformationData: MessageInformationData) : SimpleAction(R.string.message_view_reaction, R.drawable.ic_view_reactions)
}

data class MessageMenuState(
val roomId: String,
@ -68,24 +87,6 @@ class MessageMenuViewModel @AssistedInject constructor(@Assisted initialState: M
private val informationData: MessageInformationData = initialState.informationData

companion object : MvRxViewModelFactory<MessageMenuViewModel, MessageMenuState> {

const val ACTION_ADD_REACTION = "add_reaction"
const val ACTION_COPY = "copy"
const val ACTION_EDIT = "edit"
const val ACTION_QUOTE = "quote"
const val ACTION_REPLY = "reply"
const val ACTION_SHARE = "share"
const val ACTION_RESEND = "resend"
const val ACTION_REMOVE = "remove"
const val ACTION_DELETE = "delete"
const val ACTION_CANCEL = "cancel"
const val VIEW_SOURCE = "VIEW_SOURCE"
const val VIEW_DECRYPTED_SOURCE = "VIEW_DECRYPTED_SOURCE"
const val ACTION_COPY_PERMALINK = "ACTION_COPY_PERMALINK"
const val ACTION_FLAG = "ACTION_FLAG"
const val ACTION_QUICK_REACT = "ACTION_QUICK_REACT"
const val ACTION_VIEW_REACTIONS = "ACTION_VIEW_REACTIONS"

override fun create(viewModelContext: ViewModelContext, state: MessageMenuState): MessageMenuViewModel? {
val fragment: MessageMenuFragment = (viewModelContext as FragmentViewModelContext).fragment()
return fragment.messageMenuViewModelFactory.create(state)
@ -99,75 +100,64 @@ class MessageMenuViewModel @AssistedInject constructor(@Assisted initialState: M
private fun observeEvent() {
RxRoom(room)
.liveTimelineEvent(eventId)
?.map {
.map {
actionsForEvent(it)
}
?.execute {
.execute {
copy(actions = it)
}
}

private fun actionsForEvent(event: TimelineEvent): List<SimpleAction> {

val messageContent: MessageContent? = event.annotations?.editSummary?.aggregatedContent.toModel()
?: event.root.getClearContent().toModel()
val type = messageContent?.type

return if (event.root.sendState.hasFailed()) {
arrayListOf<SimpleAction>().apply {
return arrayListOf<SimpleAction>().apply {
if (event.root.sendState.hasFailed()) {
if (canRetry(event)) {
this.add(SimpleAction(ACTION_RESEND, R.string.global_retry, R.drawable.ic_refresh_cw, eventId))
add(SimpleAction.Resend(eventId))
}
this.add(SimpleAction(ACTION_REMOVE, R.string.remove, R.drawable.ic_trash, eventId))
}
} else if (event.root.sendState.isSending()) {
//TODO is uploading attachment?
arrayListOf<SimpleAction>().apply {
add(SimpleAction.Remove(eventId))
} else if (event.root.sendState.isSending()) {
//TODO is uploading attachment?
if (canCancel(event)) {
this.add(SimpleAction(ACTION_CANCEL, R.string.cancel, R.drawable.ic_close_round, eventId))
add(SimpleAction.Cancel(eventId))
}
}
} else {
arrayListOf<SimpleAction>().apply {

} else {
if (!event.root.isRedacted()) {

if (canReply(event, messageContent)) {
add(SimpleAction(ACTION_REPLY, R.string.reply, R.drawable.ic_reply, eventId))
add(SimpleAction.Reply(eventId))
}

if (canEdit(event, session.myUserId)) {
add(SimpleAction(ACTION_EDIT, R.string.edit, R.drawable.ic_edit, eventId))
add(SimpleAction.Edit(eventId))
}

if (canRedact(event, session.myUserId)) {
add(SimpleAction(ACTION_DELETE, R.string.delete, R.drawable.ic_delete, eventId))
add(SimpleAction.Delete(eventId))
}

if (canCopy(type)) {
//TODO copy images? html? see ClipBoard
add(SimpleAction(ACTION_COPY, R.string.copy, R.drawable.ic_copy, messageContent!!.body))
add(SimpleAction.Copy(messageContent!!.body))
}

if (event.canReact()) {
add(SimpleAction(ACTION_ADD_REACTION, R.string.message_add_reaction, R.drawable.ic_add_reaction, eventId))
add(SimpleAction.AddReaction(eventId))
}

if (canQuote(event, messageContent)) {
add(SimpleAction(ACTION_QUOTE, R.string.quote, R.drawable.ic_quote, eventId))
add(SimpleAction.Quote(eventId))
}

if (canViewReactions(event)) {
add(SimpleAction(ACTION_VIEW_REACTIONS, R.string.message_view_reaction, R.drawable.ic_view_reactions, informationData))
add(SimpleAction.ViewReactions(informationData))
}

if (canShare(type)) {
if (messageContent is MessageImageContent) {
add(
SimpleAction(ACTION_SHARE,
R.string.share, R.drawable.ic_share,
session.contentUrlResolver().resolveFullSize(messageContent.url))
)
add(SimpleAction.Share(session.contentUrlResolver().resolveFullSize(messageContent.url)))
}
//TODO
}
@ -181,17 +171,17 @@ class MessageMenuViewModel @AssistedInject constructor(@Assisted initialState: M
}
}

add(SimpleAction(VIEW_SOURCE, R.string.view_source, R.drawable.ic_view_source, event.root.toContentStringWithIndent()))
add(SimpleAction.ViewSource(event.root.toContentStringWithIndent()))
if (event.isEncrypted()) {
val decryptedContent = event.root.toClearContentStringWithIndent()
?: stringProvider.getString(R.string.encryption_information_decryption_error)
add(SimpleAction(VIEW_DECRYPTED_SOURCE, R.string.view_decrypted_source, R.drawable.ic_view_source, decryptedContent))
add(SimpleAction.ViewDecryptedSource(decryptedContent))
}
add(SimpleAction(ACTION_COPY_PERMALINK, R.string.permalink, R.drawable.ic_permalink, event.root.eventId))
add(SimpleAction.CopyPermalink(eventId))

if (session.myUserId != event.root.senderId && event.root.getClearType() == EventType.MESSAGE) {
//not sent by me
add(SimpleAction(ACTION_FLAG, R.string.report_content, R.drawable.ic_flag, event.root.eventId))
add(SimpleAction.Flag(eventId))
}
}
}
@ -269,9 +259,7 @@ class MessageMenuViewModel @AssistedInject constructor(@Assisted initialState: M
MessageType.MSGTYPE_NOTICE,
MessageType.MSGTYPE_EMOTE,
MessageType.FORMAT_MATRIX_HTML,
MessageType.MSGTYPE_LOCATION -> {
true
}
MessageType.MSGTYPE_LOCATION -> true
else -> false
}
}
@ -281,9 +269,7 @@ class MessageMenuViewModel @AssistedInject constructor(@Assisted initialState: M
return when (type) {
MessageType.MSGTYPE_IMAGE,
MessageType.MSGTYPE_AUDIO,
MessageType.MSGTYPE_VIDEO -> {
true
}
MessageType.MSGTYPE_VIDEO -> true
else -> false
}
}

View File

@ -166,11 +166,7 @@ abstract class AbsMessageItem<H : AbsMessageItem.Holder> : BaseEventItem<H>() {
root.isClickable = informationData.sendState.isSent()
val state = if (informationData.hasPendingEdits) SendState.UNSENT else informationData.sendState
textView?.setTextColor(colorProvider.getMessageTextColor(state))
failureIndicator?.isVisible = when (informationData.sendState) {
SendState.UNDELIVERED,
SendState.FAILED_UNKNOWN_DEVICES -> true
else -> false
}
failureIndicator?.isVisible = informationData.sendState.hasFailed()
}

abstract class Holder(@IdRes stubId: Int) : BaseHolder(stubId) {

View File

@ -53,10 +53,6 @@ abstract class MessageImageVideoItem : AbsMessageItem<MessageImageVideoItem.Hold
holder.mediaContentView.setOnLongClickListener(longClickListener)
// The sending state color will be apply to the progress text
renderSendState(holder.imageView, null, holder.failedToSendIndicator)
holder.progressLayout
if (informationData.sendState.hasFailed()) {

}
holder.playContentView.visibility = if (playable) View.VISIBLE else View.GONE
}


View File

@ -84,7 +84,7 @@ class RoomListViewModel @AssistedInject constructor(@Assisted initialState: Room
// PRIVATE METHODS *****************************************************************************

private fun handleSelectRoom(action: RoomListActions.SelectRoom) {
_openRoomLiveData.postValue(LiveEvent(action.roomSummary.roomId))
_openRoomLiveData.postLiveEvent(action.roomSummary.roomId)
}

private fun handleToggleCategory(action: RoomListActions.ToggleCategory) = setState {

View File

@ -31,6 +31,7 @@ import im.vector.matrix.android.api.session.content.ContentUrlResolver
import im.vector.matrix.android.internal.crypto.attachments.ElementToDecrypt
import im.vector.riotx.core.di.ActiveSessionHolder
import im.vector.riotx.core.glide.GlideApp
import im.vector.riotx.core.glide.GlideRequest
import im.vector.riotx.core.utils.DimensionUtils.dpToPx
import kotlinx.android.parcel.Parcelize
import timber.log.Timber
@ -67,27 +68,7 @@ class ImageContentRenderer @Inject constructor(private val activeSessionHolder:
imageView.layoutParams.height = height
imageView.layoutParams.width = width

val glideRequest = if (data.elementToDecrypt != null) {
// Encrypted image
GlideApp
.with(imageView)
.load(data)
} else {
// Clear image
val contentUrlResolver = activeSessionHolder.getActiveSession().contentUrlResolver()
val resolvedUrl = when (mode) {
Mode.FULL_SIZE -> contentUrlResolver.resolveFullSize(data.url)
Mode.THUMBNAIL -> contentUrlResolver.resolveThumbnail(data.url, width, height, ContentUrlResolver.ThumbnailMethod.SCALE)
}
//Fallback to base url
?: data.url

GlideApp
.with(imageView)
.load(resolvedUrl)
}

glideRequest
createGlideRequest(data, mode, imageView, width, height)
.dontAnimate()
.transform(RoundedCorners(dpToPx(8, imageView.context)))
.thumbnail(0.3f)
@ -95,31 +76,11 @@ class ImageContentRenderer @Inject constructor(private val activeSessionHolder:

}

fun renderFitTarget(data: Data, mode: Mode, imageView: ImageView, callback :((Boolean) -> Unit)? = null) {
fun renderFitTarget(data: Data, mode: Mode, imageView: ImageView, callback: ((Boolean) -> Unit)? = null) {
val (width, height) = processSize(data, mode)

val glideRequest = if (data.elementToDecrypt != null) {
// Encrypted image
GlideApp
.with(imageView)
.load(data)
} else {
// Clear image
val contentUrlResolver = activeSessionHolder.getActiveSession().contentUrlResolver()
val resolvedUrl = when (mode) {
Mode.FULL_SIZE -> contentUrlResolver.resolveFullSize(data.url)
Mode.THUMBNAIL -> contentUrlResolver.resolveThumbnail(data.url, width, height, ContentUrlResolver.ThumbnailMethod.SCALE)
}
//Fallback to base url
?: data.url

GlideApp
.with(imageView)
.load(resolvedUrl)
}

glideRequest
.listener(object: RequestListener<Drawable> {
createGlideRequest(data, mode, imageView, width, height)
.listener(object : RequestListener<Drawable> {
override fun onLoadFailed(e: GlideException?,
model: Any?,
target: Target<Drawable>?,
@ -140,7 +101,28 @@ class ImageContentRenderer @Inject constructor(private val activeSessionHolder:
})
.fitCenter()
.into(imageView)
}

private fun createGlideRequest(data: Data, mode: Mode, imageView: ImageView, width: Int, height: Int): GlideRequest<Drawable> {
return if (data.elementToDecrypt != null) {
// Encrypted image
GlideApp
.with(imageView)
.load(data)
} else {
// Clear image
val contentUrlResolver = activeSessionHolder.getActiveSession().contentUrlResolver()
val resolvedUrl = when (mode) {
Mode.FULL_SIZE -> contentUrlResolver.resolveFullSize(data.url)
Mode.THUMBNAIL -> contentUrlResolver.resolveThumbnail(data.url, width, height, ContentUrlResolver.ThumbnailMethod.SCALE)
}
//Fallback to base url
?: data.url

GlideApp
.with(imageView)
.load(resolvedUrl)
}
}

fun render(data: Data, imageView: BigImageView) {

View File

@ -8,7 +8,6 @@
<!-- enable window content transitions -->
<item name="android:windowContentTransitions">true</item>


<!-- specify shared element enter and exit transitions -->
<item name="android:windowSharedElementEnterTransition">@transition/image_preview_transition</item>
<item name="android:windowSharedElementExitTransition">@transition/image_preview_transition</item>