mirror of
https://github.com/element-hq/element-android
synced 2024-11-24 18:35:40 +03:00
Light refactoring.
Signed-off-by: Dominic Fischer <dominicfischer7@gmail.com>
This commit is contained in:
parent
5ab975cc5c
commit
1ac99e92a6
9 changed files with 103 additions and 161 deletions
|
@ -43,6 +43,7 @@ import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers
|
||||||
import kotlinx.coroutines.GlobalScope
|
import kotlinx.coroutines.GlobalScope
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
import java.lang.Exception
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import kotlin.collections.HashMap
|
import kotlin.collections.HashMap
|
||||||
|
@ -166,72 +167,59 @@ internal class DefaultSasVerificationService @Inject constructor(private val cre
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// Download device keys prior to everything
|
// Download device keys prior to everything
|
||||||
checkKeysAreDownloaded(
|
if (checkKeysAreDownloaded(otherUserId!!, startReq) != null) {
|
||||||
otherUserId!!,
|
Timber.v("## SAS onStartRequestReceived ${startReq.transactionID!!}")
|
||||||
startReq,
|
val tid = startReq.transactionID!!
|
||||||
success = {
|
val existing = getExistingTransaction(otherUserId, tid)
|
||||||
Timber.v("## SAS onStartRequestReceived ${startReq.transactionID!!}")
|
val existingTxs = getExistingTransactionsForUser(otherUserId)
|
||||||
val tid = startReq.transactionID!!
|
if (existing != null) {
|
||||||
val existing = getExistingTransaction(otherUserId, tid)
|
// should cancel both!
|
||||||
val existingTxs = getExistingTransactionsForUser(otherUserId)
|
Timber.v("## SAS onStartRequestReceived - Request exist with same if ${startReq.transactionID!!}")
|
||||||
if (existing != null) {
|
existing.cancel(CancelCode.UnexpectedMessage)
|
||||||
// should cancel both!
|
cancelTransaction(tid, otherUserId, startReq.fromDevice!!, CancelCode.UnexpectedMessage)
|
||||||
Timber.v("## SAS onStartRequestReceived - Request exist with same if ${startReq.transactionID!!}")
|
} else if (existingTxs?.isEmpty() == false) {
|
||||||
existing.cancel(CancelCode.UnexpectedMessage)
|
Timber.v("## SAS onStartRequestReceived - There is already a transaction with this user ${startReq.transactionID!!}")
|
||||||
cancelTransaction(tid, otherUserId, startReq.fromDevice!!, CancelCode.UnexpectedMessage)
|
// Multiple keyshares between two devices: any two devices may only have at most one key verification in flight at a time.
|
||||||
} else if (existingTxs?.isEmpty() == false) {
|
existingTxs.forEach {
|
||||||
Timber.v("## SAS onStartRequestReceived - There is already a transaction with this user ${startReq.transactionID!!}")
|
it.cancel(CancelCode.UnexpectedMessage)
|
||||||
// Multiple keyshares between two devices: any two devices may only have at most one key verification in flight at a time.
|
}
|
||||||
existingTxs.forEach {
|
cancelTransaction(tid, otherUserId, startReq.fromDevice!!, CancelCode.UnexpectedMessage)
|
||||||
it.cancel(CancelCode.UnexpectedMessage)
|
} else {
|
||||||
}
|
// Ok we can create
|
||||||
cancelTransaction(tid, otherUserId, startReq.fromDevice!!, CancelCode.UnexpectedMessage)
|
if (KeyVerificationStart.VERIF_METHOD_SAS == startReq.method) {
|
||||||
} else {
|
Timber.v("## SAS onStartRequestReceived - request accepted ${startReq.transactionID!!}")
|
||||||
// Ok we can create
|
val tx = IncomingSASVerificationTransaction(
|
||||||
if (KeyVerificationStart.VERIF_METHOD_SAS == startReq.method) {
|
this,
|
||||||
Timber.v("## SAS onStartRequestReceived - request accepted ${startReq.transactionID!!}")
|
setDeviceVerificationAction,
|
||||||
val tx = IncomingSASVerificationTransaction(
|
credentials,
|
||||||
this,
|
cryptoStore,
|
||||||
setDeviceVerificationAction,
|
sendToDeviceTask,
|
||||||
credentials,
|
taskExecutor,
|
||||||
cryptoStore,
|
myDeviceInfoHolder.get().myDevice.fingerprint()!!,
|
||||||
sendToDeviceTask,
|
startReq.transactionID!!,
|
||||||
taskExecutor,
|
otherUserId)
|
||||||
myDeviceInfoHolder.get().myDevice.fingerprint()!!,
|
addTransaction(tx)
|
||||||
startReq.transactionID!!,
|
tx.acceptToDeviceEvent(otherUserId, startReq)
|
||||||
otherUserId)
|
} else {
|
||||||
addTransaction(tx)
|
Timber.e("## SAS onStartRequestReceived - unknown method ${startReq.method}")
|
||||||
tx.acceptToDeviceEvent(otherUserId, startReq)
|
cancelTransaction(tid, otherUserId, startReq.fromDevice
|
||||||
} else {
|
?: event.getSenderKey()!!, CancelCode.UnknownMethod)
|
||||||
Timber.e("## SAS onStartRequestReceived - unknown method ${startReq.method}")
|
}
|
||||||
cancelTransaction(tid, otherUserId, startReq.fromDevice
|
}
|
||||||
?: event.getSenderKey()!!, CancelCode.UnknownMethod)
|
} else {
|
||||||
}
|
cancelTransaction(startReq.transactionID!!, otherUserId, startReq.fromDevice!!, CancelCode.UnexpectedMessage)
|
||||||
}
|
}
|
||||||
},
|
|
||||||
error = {
|
|
||||||
cancelTransaction(startReq.transactionID!!, otherUserId, startReq.fromDevice!!, CancelCode.UnexpectedMessage)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun checkKeysAreDownloaded(otherUserId: String,
|
private suspend fun checkKeysAreDownloaded(otherUserId: String,
|
||||||
startReq: KeyVerificationStart,
|
startReq: KeyVerificationStart): MXUsersDevicesMap<MXDeviceInfo>? {
|
||||||
success: (MXUsersDevicesMap<MXDeviceInfo>) -> Unit,
|
return try {
|
||||||
error: () -> Unit) {
|
val keys = deviceListManager.downloadKeys(listOf(otherUserId), true)
|
||||||
runCatching {
|
val deviceIds = keys.getUserDeviceIds(otherUserId) ?: return null
|
||||||
deviceListManager.downloadKeys(listOf(otherUserId), true)
|
keys.takeIf { deviceIds.contains(startReq.fromDevice) }
|
||||||
}.fold(
|
} catch (e: Exception) {
|
||||||
{
|
null
|
||||||
if (it.getUserDeviceIds(otherUserId)?.contains(startReq.fromDevice) == true) {
|
}
|
||||||
success(it)
|
|
||||||
} else {
|
|
||||||
error()
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
error()
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun onCancelReceived(event: Event) {
|
private suspend fun onCancelReceived(event: Event) {
|
||||||
|
|
|
@ -39,7 +39,7 @@ import im.vector.matrix.android.internal.session.room.RoomSummaryUpdater
|
||||||
import im.vector.matrix.android.internal.util.StringProvider
|
import im.vector.matrix.android.internal.util.StringProvider
|
||||||
import org.commonmark.parser.Parser
|
import org.commonmark.parser.Parser
|
||||||
import org.commonmark.renderer.html.HtmlRenderer
|
import org.commonmark.renderer.html.HtmlRenderer
|
||||||
import java.util.*
|
import java.util.UUID
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -30,15 +30,10 @@ import java.io.File
|
||||||
*/
|
*/
|
||||||
@WorkerThread
|
@WorkerThread
|
||||||
fun writeToFile(str: String, file: File): Try<Unit> {
|
fun writeToFile(str: String, file: File): Try<Unit> {
|
||||||
return Try {
|
return Try<Unit> {
|
||||||
val sink = file.sink()
|
file.sink().buffer().use {
|
||||||
|
it.writeString(str, Charsets.UTF_8)
|
||||||
val bufferedSink = sink.buffer()
|
}
|
||||||
|
|
||||||
bufferedSink.writeString(str, Charsets.UTF_8)
|
|
||||||
|
|
||||||
bufferedSink.close()
|
|
||||||
sink.close()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -47,15 +42,10 @@ fun writeToFile(str: String, file: File): Try<Unit> {
|
||||||
*/
|
*/
|
||||||
@WorkerThread
|
@WorkerThread
|
||||||
fun writeToFile(data: ByteArray, file: File): Try<Unit> {
|
fun writeToFile(data: ByteArray, file: File): Try<Unit> {
|
||||||
return Try {
|
return Try<Unit> {
|
||||||
val sink = file.sink()
|
file.sink().buffer().use {
|
||||||
|
it.write(data)
|
||||||
val bufferedSink = sink.buffer()
|
}
|
||||||
|
|
||||||
bufferedSink.write(data)
|
|
||||||
|
|
||||||
bufferedSink.close()
|
|
||||||
sink.close()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,7 +17,6 @@
|
||||||
package im.vector.riotx.core.images
|
package im.vector.riotx.core.images
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.database.Cursor
|
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.provider.MediaStore
|
import android.provider.MediaStore
|
||||||
import androidx.exifinterface.media.ExifInterface
|
import androidx.exifinterface.media.ExifInterface
|
||||||
|
@ -37,26 +36,24 @@ class ImageTools @Inject constructor(private val context: Context) {
|
||||||
|
|
||||||
if (uri.scheme == "content") {
|
if (uri.scheme == "content") {
|
||||||
val proj = arrayOf(MediaStore.Images.Media.DATA)
|
val proj = arrayOf(MediaStore.Images.Media.DATA)
|
||||||
var cursor: Cursor? = null
|
|
||||||
try {
|
try {
|
||||||
cursor = context.contentResolver.query(uri, proj, null, null, null)
|
val cursor = context.contentResolver.query(uri, proj, null, null, null)
|
||||||
if (cursor != null && cursor.count > 0) {
|
cursor?.use {
|
||||||
cursor.moveToFirst()
|
if (it.moveToFirst()) {
|
||||||
val idxData = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA)
|
val idxData = it.getColumnIndexOrThrow(MediaStore.Images.Media.DATA)
|
||||||
val path = cursor.getString(idxData)
|
val path = it.getString(idxData)
|
||||||
if (path.isNullOrBlank()) {
|
if (path.isNullOrBlank()) {
|
||||||
Timber.w("Cannot find path in media db for uri $uri")
|
Timber.w("Cannot find path in media db for uri $uri")
|
||||||
return orientation
|
return orientation
|
||||||
|
}
|
||||||
|
val exif = ExifInterface(path)
|
||||||
|
orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_UNDEFINED)
|
||||||
}
|
}
|
||||||
val exif = ExifInterface(path)
|
|
||||||
orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_UNDEFINED)
|
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
// eg SecurityException from com.google.android.apps.photos.content.GooglePhotosImageProvider URIs
|
// eg SecurityException from com.google.android.apps.photos.content.GooglePhotosImageProvider URIs
|
||||||
// eg IOException from trying to parse the returned path as a file when it is an http uri.
|
// eg IOException from trying to parse the returned path as a file when it is an http uri.
|
||||||
Timber.e(e, "Cannot get orientation for bitmap")
|
Timber.e(e, "Cannot get orientation for bitmap")
|
||||||
} finally {
|
|
||||||
cursor?.close()
|
|
||||||
}
|
}
|
||||||
} else if (uri.scheme == "file") {
|
} else if (uri.scheme == "file") {
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -17,28 +17,17 @@
|
||||||
package im.vector.riotx.core.intent
|
package im.vector.riotx.core.intent
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.database.Cursor
|
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.provider.OpenableColumns
|
import android.provider.OpenableColumns
|
||||||
|
|
||||||
fun getFilenameFromUri(context: Context?, uri: Uri): String? {
|
fun getFilenameFromUri(context: Context?, uri: Uri): String? {
|
||||||
var result: String? = null
|
|
||||||
if (context != null && uri.scheme == "content") {
|
if (context != null && uri.scheme == "content") {
|
||||||
val cursor: Cursor? = context.contentResolver.query(uri, null, null, null, null)
|
val cursor = context.contentResolver.query(uri, null, null, null, null)
|
||||||
try {
|
cursor?.use {
|
||||||
if (cursor != null && cursor.moveToFirst()) {
|
if (it.moveToFirst()) {
|
||||||
result = cursor.getString(cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME))
|
return it.getString(it.getColumnIndex(OpenableColumns.DISPLAY_NAME))
|
||||||
}
|
}
|
||||||
} finally {
|
|
||||||
cursor?.close()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (result == null) {
|
return uri.path?.substringAfterLast('/')
|
||||||
result = uri.path
|
|
||||||
val cut = result?.lastIndexOf('/') ?: -1
|
|
||||||
if (cut != -1) {
|
|
||||||
result = result?.substring(cut + 1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,10 +18,10 @@ package im.vector.riotx.features.crypto.keys
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.os.Environment
|
import android.os.Environment
|
||||||
import arrow.core.Try
|
|
||||||
import im.vector.matrix.android.api.MatrixCallback
|
import im.vector.matrix.android.api.MatrixCallback
|
||||||
import im.vector.matrix.android.api.session.Session
|
import im.vector.matrix.android.api.session.Session
|
||||||
import im.vector.matrix.android.internal.extensions.foldToCallback
|
import im.vector.matrix.android.internal.extensions.foldToCallback
|
||||||
|
import im.vector.matrix.android.internal.util.awaitCallback
|
||||||
import im.vector.riotx.core.files.addEntryToDownloadManager
|
import im.vector.riotx.core.files.addEntryToDownloadManager
|
||||||
import im.vector.riotx.core.files.writeToFile
|
import im.vector.riotx.core.files.writeToFile
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
@ -36,28 +36,20 @@ class KeysExporter(private val session: Session) {
|
||||||
* Export keys and return the file path with the callback
|
* Export keys and return the file path with the callback
|
||||||
*/
|
*/
|
||||||
fun export(context: Context, password: String, callback: MatrixCallback<String>) {
|
fun export(context: Context, password: String, callback: MatrixCallback<String>) {
|
||||||
session.exportRoomKeys(password, object : MatrixCallback<ByteArray> {
|
GlobalScope.launch(Dispatchers.Main) {
|
||||||
override fun onSuccess(data: ByteArray) {
|
runCatching {
|
||||||
GlobalScope.launch(Dispatchers.Main) {
|
val data = awaitCallback<ByteArray> { session.exportRoomKeys(password, it) }
|
||||||
withContext(Dispatchers.IO) {
|
withContext(Dispatchers.IO) {
|
||||||
Try {
|
val parentDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)
|
||||||
val parentDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)
|
val file = File(parentDir, "riotx-keys-" + System.currentTimeMillis() + ".txt")
|
||||||
val file = File(parentDir, "riotx-keys-" + System.currentTimeMillis() + ".txt")
|
|
||||||
|
|
||||||
writeToFile(data, file)
|
writeToFile(data, file)
|
||||||
|
|
||||||
addEntryToDownloadManager(context, file, "text/plain")
|
addEntryToDownloadManager(context, file, "text/plain")
|
||||||
|
|
||||||
file.absolutePath
|
file.absolutePath
|
||||||
}
|
|
||||||
}
|
|
||||||
.foldToCallback(callback)
|
|
||||||
}
|
}
|
||||||
}
|
}.foldToCallback(callback)
|
||||||
|
}
|
||||||
override fun onFailure(failure: Throwable) {
|
|
||||||
callback.onFailure(failure)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,10 +18,11 @@ package im.vector.riotx.features.crypto.keys
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import arrow.core.Try
|
|
||||||
import im.vector.matrix.android.api.MatrixCallback
|
import im.vector.matrix.android.api.MatrixCallback
|
||||||
import im.vector.matrix.android.api.session.Session
|
import im.vector.matrix.android.api.session.Session
|
||||||
import im.vector.matrix.android.internal.crypto.model.ImportRoomKeysResult
|
import im.vector.matrix.android.internal.crypto.model.ImportRoomKeysResult
|
||||||
|
import im.vector.matrix.android.internal.extensions.foldToCallback
|
||||||
|
import im.vector.matrix.android.internal.util.awaitCallback
|
||||||
import im.vector.riotx.core.intent.getMimeTypeFromUri
|
import im.vector.riotx.core.intent.getMimeTypeFromUri
|
||||||
import im.vector.riotx.core.resources.openResource
|
import im.vector.riotx.core.resources.openResource
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
@ -41,8 +42,8 @@ class KeysImporter(private val session: Session) {
|
||||||
password: String,
|
password: String,
|
||||||
callback: MatrixCallback<ImportRoomKeysResult>) {
|
callback: MatrixCallback<ImportRoomKeysResult>) {
|
||||||
GlobalScope.launch(Dispatchers.Main) {
|
GlobalScope.launch(Dispatchers.Main) {
|
||||||
withContext(Dispatchers.IO) {
|
runCatching {
|
||||||
Try {
|
withContext(Dispatchers.IO) {
|
||||||
val resource = openResource(context, uri, mimetype ?: getMimeTypeFromUri(context, uri))
|
val resource = openResource(context, uri, mimetype ?: getMimeTypeFromUri(context, uri))
|
||||||
|
|
||||||
if (resource?.mContentStream == null) {
|
if (resource?.mContentStream == null) {
|
||||||
|
@ -51,33 +52,17 @@ class KeysImporter(private val session: Session) {
|
||||||
|
|
||||||
val data: ByteArray
|
val data: ByteArray
|
||||||
try {
|
try {
|
||||||
data = ByteArray(resource.mContentStream!!.available())
|
data = resource.mContentStream!!.use { it.readBytes() }
|
||||||
resource.mContentStream!!.read(data)
|
|
||||||
resource.mContentStream!!.close()
|
|
||||||
|
|
||||||
data
|
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
try {
|
Timber.e(e, "## importKeys()")
|
||||||
resource.mContentStream!!.close()
|
|
||||||
} catch (e2: Exception) {
|
|
||||||
Timber.e(e2, "## importKeys()")
|
|
||||||
}
|
|
||||||
|
|
||||||
throw e
|
throw e
|
||||||
}
|
}
|
||||||
|
|
||||||
|
awaitCallback<ImportRoomKeysResult> {
|
||||||
|
session.importRoomKeys(data, password, null, it)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}.foldToCallback(callback)
|
||||||
.fold(
|
|
||||||
{
|
|
||||||
callback.onFailure(it)
|
|
||||||
},
|
|
||||||
{ byteArray ->
|
|
||||||
session.importRoomKeys(byteArray,
|
|
||||||
password,
|
|
||||||
null,
|
|
||||||
callback)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -170,8 +170,8 @@ class KeysBackupSetupStep3Fragment : VectorBaseFragment() {
|
||||||
|
|
||||||
private fun exportRecoveryKeyToFile(data: String) {
|
private fun exportRecoveryKeyToFile(data: String) {
|
||||||
GlobalScope.launch(Dispatchers.Main) {
|
GlobalScope.launch(Dispatchers.Main) {
|
||||||
withContext(Dispatchers.IO) {
|
Try {
|
||||||
Try {
|
withContext(Dispatchers.IO) {
|
||||||
val parentDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)
|
val parentDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)
|
||||||
val file = File(parentDir, "recovery-key-" + System.currentTimeMillis() + ".txt")
|
val file = File(parentDir, "recovery-key-" + System.currentTimeMillis() + ".txt")
|
||||||
|
|
||||||
|
|
|
@ -42,7 +42,8 @@ import im.vector.riotx.features.home.room.detail.timeline.format.NoticeEventForm
|
||||||
import im.vector.riotx.features.home.room.detail.timeline.item.MessageInformationData
|
import im.vector.riotx.features.home.room.detail.timeline.item.MessageInformationData
|
||||||
import im.vector.riotx.features.html.EventHtmlRenderer
|
import im.vector.riotx.features.html.EventHtmlRenderer
|
||||||
import java.text.SimpleDateFormat
|
import java.text.SimpleDateFormat
|
||||||
import java.util.*
|
import java.util.Date
|
||||||
|
import java.util.Locale
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Quick reactions state
|
* Quick reactions state
|
||||||
|
|
Loading…
Reference in a new issue