mirror of
https://github.com/SchildiChat/SchildiChat-android.git
synced 2024-11-21 17:05:39 +03:00
Attachments: use a lib which handles for us all the intent stuff.
This commit is contained in:
parent
3073470c38
commit
0a9ebb6bf6
7 changed files with 222 additions and 245 deletions
|
@ -12,7 +12,7 @@ buildscript {
|
|||
}
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:3.5.0'
|
||||
classpath 'com.android.tools.build:gradle:3.5.1'
|
||||
classpath 'com.google.gms:google-services:4.3.2'
|
||||
classpath "com.airbnb.okreplay:gradle-plugin:1.5.0"
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||
|
|
|
@ -320,6 +320,7 @@ dependencies {
|
|||
|
||||
// File picker
|
||||
implementation 'com.github.jaiselrahman:FilePicker:1.2.2'
|
||||
implementation 'com.kbeanie:multipicker:1.6@aar'
|
||||
|
||||
// DI
|
||||
implementation "com.google.dagger:dagger:$daggerVersion"
|
||||
|
|
|
@ -1,50 +0,0 @@
|
|||
/*
|
||||
* 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.riotx.features.attachments
|
||||
|
||||
import im.vector.riotx.core.resources.MIME_TYPE_ALL_CONTENT
|
||||
|
||||
data class Attachment(val path: String,
|
||||
val mimeType: String,
|
||||
val name: String? = "",
|
||||
val width: Long? = 0,
|
||||
val height: Long? = 0,
|
||||
val size: Long = 0,
|
||||
val duration: Long? = 0,
|
||||
val date: Long = 0) {
|
||||
|
||||
val type: Int
|
||||
get() {
|
||||
if (mimeType == null) {
|
||||
return TYPE_FILE
|
||||
}
|
||||
return when {
|
||||
mimeType.startsWith("image/") -> TYPE_IMAGE
|
||||
mimeType.startsWith("video/") -> TYPE_VIDEO
|
||||
mimeType.startsWith("audio/")
|
||||
-> TYPE_AUDIO
|
||||
else -> TYPE_FILE
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
val TYPE_FILE = 0
|
||||
val TYPE_IMAGE = 1
|
||||
val TYPE_AUDIO = 2
|
||||
val TYPE_VIDEO = 3
|
||||
}
|
||||
}
|
|
@ -15,138 +15,174 @@
|
|||
*/
|
||||
package im.vector.riotx.features.attachments
|
||||
|
||||
import android.content.ActivityNotFoundException
|
||||
import android.content.Context
|
||||
import android.app.Activity
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.os.Environment
|
||||
import android.provider.MediaStore
|
||||
import android.provider.OpenableColumns
|
||||
import androidx.core.content.FileProvider
|
||||
import android.os.Bundle
|
||||
import androidx.fragment.app.Fragment
|
||||
import im.vector.riotx.BuildConfig
|
||||
import im.vector.riotx.core.resources.MIME_TYPE_ALL_CONTENT
|
||||
import timber.log.Timber
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.*
|
||||
import com.kbeanie.multipicker.api.CameraImagePicker
|
||||
import com.kbeanie.multipicker.api.FilePicker
|
||||
import com.kbeanie.multipicker.api.ImagePicker
|
||||
import com.kbeanie.multipicker.api.Picker.*
|
||||
import com.kbeanie.multipicker.api.callbacks.FilePickerCallback
|
||||
import com.kbeanie.multipicker.api.callbacks.ImagePickerCallback
|
||||
import com.kbeanie.multipicker.api.entity.ChosenFile
|
||||
import com.kbeanie.multipicker.api.entity.ChosenImage
|
||||
import com.kbeanie.multipicker.api.entity.ChosenVideo
|
||||
import com.kbeanie.multipicker.core.PickerManager
|
||||
import im.vector.matrix.android.api.session.content.ContentAttachmentData
|
||||
import im.vector.riotx.core.platform.Restorable
|
||||
|
||||
private const val CAPTURE_PATH_KEY = "CAPTURE_PATH_KEY"
|
||||
|
||||
class AttachmentsHelper(private val context: Context) {
|
||||
class AttachmentsHelper(private val fragment: Fragment, private val callback: Callback) : Restorable {
|
||||
|
||||
private var capturePath: String? = null
|
||||
|
||||
fun selectFile(fragment: Fragment, requestCode: Int) {
|
||||
selectMediaType(fragment, "*/*", null, requestCode)
|
||||
interface Callback {
|
||||
fun onAttachmentsReady(attachments: List<ContentAttachmentData>)
|
||||
fun onAttachmentsProcessFailed()
|
||||
}
|
||||
|
||||
fun selectGallery(fragment: Fragment, requestCode: Int) {
|
||||
selectMediaType(fragment, "image/*", arrayOf("image/*", "video/*"), requestCode)
|
||||
}
|
||||
private val attachmentsPickerCallback = AttachmentsPickerCallback(callback)
|
||||
|
||||
fun openCamera(fragment: Fragment, requestCode: Int) {
|
||||
dispatchTakePictureIntent(fragment, requestCode)
|
||||
}
|
||||
|
||||
|
||||
fun handleOpenCameraResult(): List<Attachment> {
|
||||
val attachment = getAttachmentFromContentResolver(Uri.parse(capturePath))
|
||||
return if (attachment == null) {
|
||||
emptyList()
|
||||
} else {
|
||||
listOf(attachment)
|
||||
private val imagePicker by lazy {
|
||||
ImagePicker(fragment).also {
|
||||
it.setImagePickerCallback(attachmentsPickerCallback)
|
||||
it.allowMultiple()
|
||||
}
|
||||
}
|
||||
|
||||
fun handleSelectResult(data: Intent?): List<Attachment> {
|
||||
val clipData = data?.clipData
|
||||
if (clipData != null) {
|
||||
return (0 until clipData.itemCount).map {
|
||||
clipData.getItemAt(it)
|
||||
}.mapNotNull {
|
||||
getAttachmentFromContentResolver(it.uri)
|
||||
private val cameraImagePicker by lazy {
|
||||
CameraImagePicker(fragment).also {
|
||||
it.setImagePickerCallback(attachmentsPickerCallback)
|
||||
}
|
||||
}
|
||||
|
||||
private val filePicker by lazy {
|
||||
FilePicker(fragment).also {
|
||||
it.allowMultiple()
|
||||
it.setFilePickerCallback(attachmentsPickerCallback)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onSaveInstanceState(outState: Bundle) {
|
||||
capturePath?.also {
|
||||
outState.putString(CAPTURE_PATH_KEY, it)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onRestoreInstanceState(savedInstanceState: Bundle?) {
|
||||
capturePath = savedInstanceState?.getString(CAPTURE_PATH_KEY)
|
||||
}
|
||||
|
||||
var capturePath: String? = null
|
||||
private set
|
||||
|
||||
fun selectFile() {
|
||||
filePicker.pickFile()
|
||||
}
|
||||
|
||||
fun selectGallery() {
|
||||
imagePicker.pickImage()
|
||||
}
|
||||
|
||||
fun openCamera() {
|
||||
capturePath = cameraImagePicker.pickImage()
|
||||
}
|
||||
|
||||
fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?): Boolean {
|
||||
if (resultCode == Activity.RESULT_OK) {
|
||||
val pickerManager = getPickerManager(requestCode)
|
||||
if (pickerManager != null) {
|
||||
pickerManager.submit(data)
|
||||
return true
|
||||
}
|
||||
} else {
|
||||
val uri = data?.data ?: return emptyList()
|
||||
val attachment = getAttachmentFromContentResolver(uri)
|
||||
return if (attachment == null) {
|
||||
emptyList()
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
private fun getPickerManager(requestCode: Int): PickerManager? {
|
||||
return when (requestCode) {
|
||||
PICK_IMAGE_DEVICE -> imagePicker
|
||||
PICK_IMAGE_CAMERA -> cameraImagePicker
|
||||
PICK_FILE -> filePicker
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
private inner class AttachmentsPickerCallback(private val callback: Callback) : ImagePickerCallback, FilePickerCallback {
|
||||
|
||||
override fun onFilesChosen(files: MutableList<ChosenFile>?) {
|
||||
if (files.isNullOrEmpty()) {
|
||||
callback.onAttachmentsProcessFailed()
|
||||
} else {
|
||||
listOf(attachment)
|
||||
val attachments = files.map {
|
||||
it.toContentAttachmentData()
|
||||
}
|
||||
callback.onAttachmentsReady(attachments)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun selectMediaType(fragment: Fragment, type: String, extraMimeType: Array<String>?, requestCode: Int) {
|
||||
val intent = Intent()
|
||||
intent.type = type
|
||||
intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true)
|
||||
if (extraMimeType != null) {
|
||||
intent.putExtra(Intent.EXTRA_MIME_TYPES, extraMimeType)
|
||||
}
|
||||
intent.action = Intent.ACTION_OPEN_DOCUMENT
|
||||
try {
|
||||
fragment.startActivityForResult(intent, requestCode)
|
||||
return
|
||||
} catch (exception: ActivityNotFoundException) {
|
||||
Timber.e(exception)
|
||||
}
|
||||
intent.action = Intent.ACTION_GET_CONTENT
|
||||
try {
|
||||
fragment.startActivityForResult(intent, requestCode)
|
||||
} catch (exception: ActivityNotFoundException) {
|
||||
Timber.e(exception)
|
||||
}
|
||||
}
|
||||
|
||||
private fun getAttachmentFromContentResolver(uri: Uri): Attachment? {
|
||||
return context.contentResolver.query(uri, null, null, null, null)?.use {
|
||||
if (it.moveToFirst()) {
|
||||
val fileName = it.getString(it.getColumnIndex(OpenableColumns.DISPLAY_NAME))
|
||||
val fileSize = it.getLong(it.getColumnIndex(OpenableColumns.SIZE))
|
||||
val mimeType = context.contentResolver.getType(uri) ?: MIME_TYPE_ALL_CONTENT
|
||||
Attachment(uri.toString(), mimeType, fileName, fileSize)
|
||||
override fun onImagesChosen(images: MutableList<ChosenImage>?) {
|
||||
if (images.isNullOrEmpty()) {
|
||||
callback.onAttachmentsProcessFailed()
|
||||
} else {
|
||||
null
|
||||
val attachments = images.map {
|
||||
it.toContentAttachmentData()
|
||||
}
|
||||
callback.onAttachmentsReady(attachments)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onError(error: String?) {
|
||||
callback.onAttachmentsProcessFailed()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Throws(IOException::class)
|
||||
private fun createImageFile(context: Context): File {
|
||||
val timeStamp = SimpleDateFormat("yyyyMMdd_HHmmss").format(Date())
|
||||
val imageFileName = "JPEG_" + timeStamp + "_"
|
||||
val storageDir = context.getExternalFilesDir(Environment.DIRECTORY_PICTURES)
|
||||
val image = File.createTempFile(
|
||||
imageFileName, /* prefix */
|
||||
".jpg", /* suffix */
|
||||
storageDir /* directory */
|
||||
private fun ChosenFile.toContentAttachmentData(): ContentAttachmentData {
|
||||
return ContentAttachmentData(
|
||||
path = originalPath,
|
||||
mimeType = mimeType,
|
||||
type = mapType(),
|
||||
size = size,
|
||||
date = createdAt.time,
|
||||
name = displayName
|
||||
)
|
||||
// Save a file: path for use with ACTION_VIEW intents
|
||||
capturePath = image.absolutePath
|
||||
return image
|
||||
}
|
||||
|
||||
private fun dispatchTakePictureIntent(fragment: Fragment, requestCode: Int) {
|
||||
val takePictureIntent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
|
||||
// Ensure that there's a camera activity to handle the intent
|
||||
if (takePictureIntent.resolveActivity(fragment.requireActivity().packageManager) != null) {
|
||||
// Create the File where the photo should go
|
||||
var photoFile: File? = null
|
||||
try {
|
||||
photoFile = createImageFile(fragment.requireContext())
|
||||
} catch (ex: IOException) {
|
||||
Timber.e(ex, "Couldn't create image file")
|
||||
}
|
||||
// Continue only if the File was successfully created
|
||||
if (photoFile != null) {
|
||||
val photoURI = FileProvider.getUriForFile(fragment.requireContext(), BuildConfig.APPLICATION_ID + ".fileProvider", photoFile)
|
||||
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI)
|
||||
fragment.startActivityForResult(takePictureIntent, requestCode)
|
||||
}
|
||||
private fun ChosenFile.mapType(): ContentAttachmentData.Type {
|
||||
return when {
|
||||
mimeType.startsWith("image/") -> ContentAttachmentData.Type.IMAGE
|
||||
mimeType.startsWith("video/") -> ContentAttachmentData.Type.VIDEO
|
||||
mimeType.startsWith("audio/") -> ContentAttachmentData.Type.AUDIO
|
||||
else -> ContentAttachmentData.Type.FILE
|
||||
}
|
||||
}
|
||||
|
||||
private fun ChosenImage.toContentAttachmentData(): ContentAttachmentData {
|
||||
return ContentAttachmentData(
|
||||
path = originalPath,
|
||||
mimeType = mimeType,
|
||||
type = mapType(),
|
||||
name = displayName,
|
||||
size = size,
|
||||
height = height.toLong(),
|
||||
width = width.toLong(),
|
||||
date = createdAt.time
|
||||
)
|
||||
}
|
||||
|
||||
private fun ChosenVideo.toContentAttachmentData(): ContentAttachmentData {
|
||||
return ContentAttachmentData(
|
||||
path = originalPath,
|
||||
mimeType = mimeType,
|
||||
type = ContentAttachmentData.Type.VIDEO,
|
||||
size = size,
|
||||
date = createdAt.time,
|
||||
height = height.toLong(),
|
||||
width = width.toLong(),
|
||||
duration = duration,
|
||||
name = displayName
|
||||
)
|
||||
}
|
||||
|
||||
}
|
|
@ -16,17 +16,17 @@
|
|||
|
||||
package im.vector.riotx.features.home.room.detail
|
||||
|
||||
import im.vector.matrix.android.api.session.content.ContentAttachmentData
|
||||
import im.vector.matrix.android.api.session.events.model.Event
|
||||
import im.vector.matrix.android.api.session.room.model.message.MessageFileContent
|
||||
import im.vector.matrix.android.api.session.room.timeline.Timeline
|
||||
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
|
||||
import im.vector.riotx.features.attachments.Attachment
|
||||
|
||||
sealed class RoomDetailActions {
|
||||
|
||||
data class SaveDraft(val draft: String) : RoomDetailActions()
|
||||
data class SendMessage(val text: String, val autoMarkdown: Boolean) : RoomDetailActions()
|
||||
data class SendMedia(val attachments: List<Attachment>) : RoomDetailActions()
|
||||
data class SendMedia(val attachments: List<ContentAttachmentData>) : RoomDetailActions()
|
||||
data class TimelineEventTurnsVisible(val event: TimelineEvent) : RoomDetailActions()
|
||||
data class TimelineEventTurnsInvisible(val event: TimelineEvent) : RoomDetailActions()
|
||||
data class LoadMoreTimelineEvents(val direction: Timeline.Direction) : RoomDetailActions()
|
||||
|
|
|
@ -49,13 +49,12 @@ import com.airbnb.mvrx.*
|
|||
import com.github.piasy.biv.BigImageViewer
|
||||
import com.github.piasy.biv.loader.ImageLoader
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import com.jaiselrahman.filepicker.activity.FilePickerActivity
|
||||
import com.jaiselrahman.filepicker.model.MediaFile
|
||||
import com.otaliastudios.autocomplete.Autocomplete
|
||||
import com.otaliastudios.autocomplete.AutocompleteCallback
|
||||
import com.otaliastudios.autocomplete.CharPolicy
|
||||
import im.vector.matrix.android.api.permalinks.PermalinkFactory
|
||||
import im.vector.matrix.android.api.session.Session
|
||||
import im.vector.matrix.android.api.session.content.ContentAttachmentData
|
||||
import im.vector.matrix.android.api.session.events.model.Event
|
||||
import im.vector.matrix.android.api.session.room.model.Membership
|
||||
import im.vector.matrix.android.api.session.room.model.message.*
|
||||
|
@ -125,10 +124,7 @@ data class RoomDetailArgs(
|
|||
) : Parcelable
|
||||
|
||||
|
||||
private const val REQUEST_CODE_SELECT_FILE = 1
|
||||
private const val REQUEST_CODE_SELECT_GALLERY = 2
|
||||
private const val REQUEST_CODE_OPEN_CAMERA = 3
|
||||
private const val REACTION_SELECT_REQUEST_CODE = 4
|
||||
private const val REACTION_SELECT_REQUEST_CODE = 0
|
||||
|
||||
class RoomDetailFragment :
|
||||
VectorBaseFragment(),
|
||||
|
@ -136,7 +132,8 @@ class RoomDetailFragment :
|
|||
AutocompleteUserPresenter.Callback,
|
||||
VectorInviteView.Callback,
|
||||
JumpToReadMarkerView.Callback,
|
||||
AttachmentTypeSelectorView.Callback {
|
||||
AttachmentTypeSelectorView.Callback,
|
||||
AttachmentsHelper.Callback {
|
||||
|
||||
companion object {
|
||||
|
||||
|
@ -198,7 +195,15 @@ class RoomDetailFragment :
|
|||
|
||||
private lateinit var actionViewModel: ActionsHandler
|
||||
private lateinit var layoutManager: LinearLayoutManager
|
||||
private lateinit var attachmentsHelper: AttachmentsHelper
|
||||
|
||||
private lateinit var _attachmentsHelper: AttachmentsHelper
|
||||
private val attachmentsHelper: AttachmentsHelper
|
||||
get() {
|
||||
if (::_attachmentsHelper.isInitialized.not()) {
|
||||
_attachmentsHelper = AttachmentsHelper(this, this).register()
|
||||
}
|
||||
return _attachmentsHelper
|
||||
}
|
||||
|
||||
@BindView(R.id.composerLayout)
|
||||
lateinit var composerLayout: TextComposerView
|
||||
|
@ -213,7 +218,6 @@ class RoomDetailFragment :
|
|||
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
||||
super.onActivityCreated(savedInstanceState)
|
||||
actionViewModel = ViewModelProviders.of(requireActivity()).get(ActionsHandler::class.java)
|
||||
attachmentsHelper = AttachmentsHelper((requireActivity()))
|
||||
setupToolbar(roomToolbar)
|
||||
setupRecyclerView()
|
||||
setupComposer()
|
||||
|
@ -302,9 +306,9 @@ class RoomDetailFragment :
|
|||
AlertDialog.Builder(requireActivity())
|
||||
.setTitle(R.string.dialog_title_error)
|
||||
.setMessage(getString(R.string.error_file_too_big,
|
||||
error.filename,
|
||||
TextUtils.formatFileSize(requireContext(), error.fileSizeInBytes),
|
||||
TextUtils.formatFileSize(requireContext(), error.homeServerLimitInBytes)
|
||||
error.filename,
|
||||
TextUtils.formatFileSize(requireContext(), error.fileSizeInBytes),
|
||||
TextUtils.formatFileSize(requireContext(), error.homeServerLimitInBytes)
|
||||
))
|
||||
.setPositiveButton(R.string.ok, null)
|
||||
.show()
|
||||
|
@ -381,11 +385,11 @@ class RoomDetailFragment :
|
|||
if (messageContent is MessageTextContent && messageContent.format == MessageType.FORMAT_MATRIX_HTML) {
|
||||
val parser = Parser.builder().build()
|
||||
val document = parser.parse(messageContent.formattedBody
|
||||
?: messageContent.body)
|
||||
?: messageContent.body)
|
||||
formattedBody = eventHtmlRenderer.render(document)
|
||||
}
|
||||
composerLayout.composerRelatedMessageContent.text = formattedBody
|
||||
?: nonFormattedBody
|
||||
?: nonFormattedBody
|
||||
|
||||
updateComposerText(defaultContent)
|
||||
|
||||
|
@ -394,11 +398,11 @@ class RoomDetailFragment :
|
|||
|
||||
|
||||
avatarRenderer.render(event.senderAvatar, event.root.senderId
|
||||
?: "", event.senderName, composerLayout.composerRelatedMessageAvatar)
|
||||
?: "", event.senderName, composerLayout.composerRelatedMessageAvatar)
|
||||
avatarRenderer.render(event.senderAvatar,
|
||||
event.root.senderId ?: "",
|
||||
event.senderName,
|
||||
composerLayout.composerRelatedMessageAvatar)
|
||||
event.root.senderId ?: "",
|
||||
event.senderName,
|
||||
composerLayout.composerRelatedMessageAvatar)
|
||||
composerLayout.expand {
|
||||
//need to do it here also when not using quick reply
|
||||
focusComposerAndShowKeyboard()
|
||||
|
@ -430,31 +434,22 @@ class RoomDetailFragment :
|
|||
}
|
||||
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
if (resultCode == RESULT_OK) {
|
||||
if (requestCode == REQUEST_CODE_OPEN_CAMERA) {
|
||||
val attachments = attachmentsHelper.handleOpenCameraResult()
|
||||
roomDetailViewModel.process(RoomDetailActions.SendMedia(attachments))
|
||||
} else if (data != null) {
|
||||
when (requestCode) {
|
||||
REQUEST_CODE_SELECT_FILE,
|
||||
REQUEST_CODE_SELECT_GALLERY -> {
|
||||
val attachments = attachmentsHelper.handleSelectResult(data)
|
||||
roomDetailViewModel.process(RoomDetailActions.SendMedia(attachments))
|
||||
}
|
||||
REACTION_SELECT_REQUEST_CODE -> {
|
||||
val eventId = data.getStringExtra(EmojiReactionPickerActivity.EXTRA_EVENT_ID)
|
||||
?: return
|
||||
val reaction = data.getStringExtra(EmojiReactionPickerActivity.EXTRA_REACTION_RESULT)
|
||||
?: return
|
||||
//TODO check if already reacted with that?
|
||||
roomDetailViewModel.process(RoomDetailActions.SendReaction(reaction, eventId))
|
||||
}
|
||||
val hasBeenHandled = attachmentsHelper.onActivityResult(requestCode, resultCode, data)
|
||||
if (!hasBeenHandled && resultCode == RESULT_OK && data != null) {
|
||||
when (requestCode) {
|
||||
REACTION_SELECT_REQUEST_CODE -> {
|
||||
val eventId = data.getStringExtra(EmojiReactionPickerActivity.EXTRA_EVENT_ID)
|
||||
?: return
|
||||
val reaction = data.getStringExtra(EmojiReactionPickerActivity.EXTRA_REACTION_RESULT)
|
||||
?: return
|
||||
//TODO check if already reacted with that?
|
||||
roomDetailViewModel.process(RoomDetailActions.SendReaction(reaction, eventId))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// PRIVATE METHODS *****************************************************************************
|
||||
// PRIVATE METHODS *****************************************************************************
|
||||
|
||||
|
||||
private fun setupRecyclerView() {
|
||||
|
@ -1086,7 +1081,7 @@ class RoomDetailFragment :
|
|||
}
|
||||
|
||||
|
||||
// VectorInviteView.Callback
|
||||
// VectorInviteView.Callback
|
||||
|
||||
override fun onAcceptInvite() {
|
||||
notificationDrawerManager.clearMemberShipNotificationForRoom(roomDetailArgs.roomId)
|
||||
|
@ -1098,7 +1093,7 @@ class RoomDetailFragment :
|
|||
roomDetailViewModel.process(RoomDetailActions.RejectInvite)
|
||||
}
|
||||
|
||||
// JumpToReadMarkerView.Callback
|
||||
// JumpToReadMarkerView.Callback
|
||||
|
||||
override fun onJumpToReadMarkerClicked(readMarkerId: String) {
|
||||
roomDetailViewModel.process(RoomDetailActions.NavigateToEvent(readMarkerId, false))
|
||||
|
@ -1108,14 +1103,25 @@ class RoomDetailFragment :
|
|||
roomDetailViewModel.process(RoomDetailActions.MarkAllAsRead)
|
||||
}
|
||||
|
||||
// AttachmentTypeSelectorView.Callback *********************************************************
|
||||
// AttachmentTypeSelectorView.Callback
|
||||
|
||||
override fun onTypeSelected(type: Int) {
|
||||
when (type) {
|
||||
AttachmentTypeSelectorView.TYPE_CAMERA -> attachmentsHelper.openCamera(this, REQUEST_CODE_OPEN_CAMERA)
|
||||
AttachmentTypeSelectorView.TYPE_FILE -> attachmentsHelper.selectFile(this, REQUEST_CODE_SELECT_FILE)
|
||||
AttachmentTypeSelectorView.TYPE_GALLERY -> attachmentsHelper.selectGallery(this, REQUEST_CODE_SELECT_GALLERY)
|
||||
AttachmentTypeSelectorView.TYPE_CAMERA -> attachmentsHelper.openCamera()
|
||||
AttachmentTypeSelectorView.TYPE_FILE -> attachmentsHelper.selectFile()
|
||||
AttachmentTypeSelectorView.TYPE_GALLERY -> attachmentsHelper.selectGallery()
|
||||
AttachmentTypeSelectorView.TYPE_STICKER -> vectorBaseActivity.notImplemented("Adding stickers")
|
||||
}
|
||||
}
|
||||
|
||||
// AttachmentsHelper.Callback
|
||||
|
||||
override fun onAttachmentsReady(attachments: List<ContentAttachmentData>) {
|
||||
Timber.v("onAttachmentsReady")
|
||||
roomDetailViewModel.process(RoomDetailActions.SendMedia(attachments))
|
||||
}
|
||||
|
||||
override fun onAttachmentsProcessFailed() {
|
||||
Timber.v("onAttachmentsProcessFailed")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -178,23 +178,23 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro
|
|||
copy(
|
||||
// Create a sendMode from a draft and retrieve the TimelineEvent
|
||||
sendMode = when (draft) {
|
||||
is UserDraft.REGULAR -> SendMode.REGULAR(draft.text)
|
||||
is UserDraft.QUOTE -> {
|
||||
room.getTimeLineEvent(draft.linkedEventId)?.let { timelineEvent ->
|
||||
SendMode.QUOTE(timelineEvent, draft.text)
|
||||
}
|
||||
}
|
||||
is UserDraft.REPLY -> {
|
||||
room.getTimeLineEvent(draft.linkedEventId)?.let { timelineEvent ->
|
||||
SendMode.REPLY(timelineEvent, draft.text)
|
||||
}
|
||||
}
|
||||
is UserDraft.EDIT -> {
|
||||
room.getTimeLineEvent(draft.linkedEventId)?.let { timelineEvent ->
|
||||
SendMode.EDIT(timelineEvent, draft.text)
|
||||
}
|
||||
}
|
||||
} ?: SendMode.REGULAR("")
|
||||
is UserDraft.REGULAR -> SendMode.REGULAR(draft.text)
|
||||
is UserDraft.QUOTE -> {
|
||||
room.getTimeLineEvent(draft.linkedEventId)?.let { timelineEvent ->
|
||||
SendMode.QUOTE(timelineEvent, draft.text)
|
||||
}
|
||||
}
|
||||
is UserDraft.REPLY -> {
|
||||
room.getTimeLineEvent(draft.linkedEventId)?.let { timelineEvent ->
|
||||
SendMode.REPLY(timelineEvent, draft.text)
|
||||
}
|
||||
}
|
||||
is UserDraft.EDIT -> {
|
||||
room.getTimeLineEvent(draft.linkedEventId)?.let { timelineEvent ->
|
||||
SendMode.EDIT(timelineEvent, draft.text)
|
||||
}
|
||||
}
|
||||
} ?: SendMode.REGULAR("")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -203,7 +203,7 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro
|
|||
|
||||
private fun handleTombstoneEvent(action: RoomDetailActions.HandleTombstoneEvent) {
|
||||
val tombstoneContent = action.event.getClearContent().toModel<RoomTombstoneContent>()
|
||||
?: return
|
||||
?: return
|
||||
|
||||
val roomId = tombstoneContent.replacementRoom ?: ""
|
||||
val isRoomJoined = session.getRoom(roomId)?.roomSummary()?.membership == Membership.JOIN
|
||||
|
@ -337,7 +337,7 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro
|
|||
is SendMode.EDIT -> {
|
||||
//is original event a reply?
|
||||
val inReplyTo = state.sendMode.timelineEvent.root.getClearContent().toModel<MessageContent>()?.relatesTo?.inReplyTo?.eventId
|
||||
?: state.sendMode.timelineEvent.root.content.toModel<EncryptedEventContent>()?.relatesTo?.inReplyTo?.eventId
|
||||
?: state.sendMode.timelineEvent.root.content.toModel<EncryptedEventContent>()?.relatesTo?.inReplyTo?.eventId
|
||||
if (inReplyTo != null) {
|
||||
//TODO check if same content?
|
||||
room.getTimeLineEvent(inReplyTo)?.let {
|
||||
|
@ -346,13 +346,13 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro
|
|||
} else {
|
||||
val messageContent: MessageContent? =
|
||||
state.sendMode.timelineEvent.annotations?.editSummary?.aggregatedContent.toModel()
|
||||
?: state.sendMode.timelineEvent.root.getClearContent().toModel()
|
||||
?: state.sendMode.timelineEvent.root.getClearContent().toModel()
|
||||
val existingBody = messageContent?.body ?: ""
|
||||
if (existingBody != action.text) {
|
||||
room.editTextMessage(state.sendMode.timelineEvent.root.eventId ?: "",
|
||||
messageContent?.type ?: MessageType.MSGTYPE_TEXT,
|
||||
action.text,
|
||||
action.autoMarkdown)
|
||||
messageContent?.type ?: MessageType.MSGTYPE_TEXT,
|
||||
action.text,
|
||||
action.autoMarkdown)
|
||||
} else {
|
||||
Timber.w("Same message content, do not send edition")
|
||||
}
|
||||
|
@ -363,7 +363,7 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro
|
|||
is SendMode.QUOTE -> {
|
||||
val messageContent: MessageContent? =
|
||||
state.sendMode.timelineEvent.annotations?.editSummary?.aggregatedContent.toModel()
|
||||
?: state.sendMode.timelineEvent.root.getClearContent().toModel()
|
||||
?: state.sendMode.timelineEvent.root.getClearContent().toModel()
|
||||
val textMsg = messageContent?.body
|
||||
|
||||
val finalText = legacyRiotQuoteText(textMsg, action.text)
|
||||
|
@ -466,24 +466,8 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro
|
|||
}
|
||||
|
||||
private fun handleSendMedia(action: RoomDetailActions.SendMedia) {
|
||||
val attachments = action.attachments.map {
|
||||
val nameWithExtension = getFilenameFromUri(null, Uri.parse(it.path))
|
||||
|
||||
ContentAttachmentData(
|
||||
size = it.size,
|
||||
duration = it.duration,
|
||||
date = it.date,
|
||||
height = it.height,
|
||||
width = it.width,
|
||||
name = nameWithExtension ?: it.name,
|
||||
path = it.path,
|
||||
mimeType = it.mimeType,
|
||||
type = ContentAttachmentData.Type.values()[it.type]
|
||||
)
|
||||
}
|
||||
|
||||
val attachments = action.attachments
|
||||
val homeServerCapabilities = session.getHomeServerCapabilities()
|
||||
|
||||
val maxUploadFileSize = homeServerCapabilities.maxUploadFileSize
|
||||
|
||||
if (maxUploadFileSize == HomeServerCapabilities.MAX_UPLOAD_FILE_SIZE_UNKNOWN) {
|
||||
|
@ -493,7 +477,7 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro
|
|||
when (val tooBigFile = attachments.find { it.size > maxUploadFileSize }) {
|
||||
null -> room.sendMedias(attachments)
|
||||
else -> _fileTooBigEvent.postValue(LiveEvent(FileTooBigError(tooBigFile.name
|
||||
?: tooBigFile.path, tooBigFile.size, maxUploadFileSize)))
|
||||
?: tooBigFile.path, tooBigFile.size, maxUploadFileSize)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue