mirror of
https://github.com/SchildiChat/SchildiChat-android.git
synced 2025-02-18 13:00:18 +03:00
Propose to edit media before sending, when coming form another application
This commit is contained in:
parent
b7ec495d6b
commit
81de914360
20 changed files with 173 additions and 56 deletions
|
@ -51,16 +51,21 @@ interface SendService {
|
||||||
/**
|
/**
|
||||||
* Method to send a media asynchronously.
|
* Method to send a media asynchronously.
|
||||||
* @param attachment the media to send
|
* @param attachment the media to send
|
||||||
|
* @param compressBeforeSending set to true to compress media before sending them
|
||||||
* @return a [Cancelable]
|
* @return a [Cancelable]
|
||||||
*/
|
*/
|
||||||
fun sendMedia(attachment: ContentAttachmentData): Cancelable
|
fun sendMedia(attachment: ContentAttachmentData,
|
||||||
|
// TODO Change to a Compression Level Enum
|
||||||
|
compressBeforeSending: Boolean): Cancelable
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Method to send a list of media asynchronously.
|
* Method to send a list of media asynchronously.
|
||||||
* @param attachments the list of media to send
|
* @param attachments the list of media to send
|
||||||
* @return a [Cancelable]
|
* @return a [Cancelable]
|
||||||
*/
|
*/
|
||||||
fun sendMedias(attachments: List<ContentAttachmentData>): Cancelable
|
fun sendMedias(attachments: List<ContentAttachmentData>,
|
||||||
|
// TODO Change to a Compression Level Enum
|
||||||
|
compressBeforeSending: Boolean): Cancelable
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Send a poll to the room.
|
* Send a poll to the room.
|
||||||
|
|
|
@ -47,6 +47,7 @@ internal class UploadContentWorker(context: Context, params: WorkerParameters) :
|
||||||
val event: Event,
|
val event: Event,
|
||||||
val attachment: ContentAttachmentData,
|
val attachment: ContentAttachmentData,
|
||||||
val isRoomEncrypted: Boolean,
|
val isRoomEncrypted: Boolean,
|
||||||
|
val compressBeforeSending: Boolean,
|
||||||
override val lastFailureMessage: String? = null
|
override val lastFailureMessage: String? = null
|
||||||
) : SessionWorkerParams
|
) : SessionWorkerParams
|
||||||
|
|
||||||
|
@ -82,6 +83,8 @@ internal class UploadContentWorker(context: Context, params: WorkerParameters) :
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO Use compressBeforeSending
|
||||||
|
|
||||||
var uploadedThumbnailUrl: String? = null
|
var uploadedThumbnailUrl: String? = null
|
||||||
var uploadedThumbnailEncryptedFileInfo: EncryptedFileInfo? = null
|
var uploadedThumbnailEncryptedFileInfo: EncryptedFileInfo? = null
|
||||||
|
|
||||||
|
|
|
@ -111,9 +111,9 @@ internal class DefaultSendService @AssistedInject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun sendMedias(attachments: List<ContentAttachmentData>): Cancelable {
|
override fun sendMedias(attachments: List<ContentAttachmentData>, compressBeforeSending: Boolean): Cancelable {
|
||||||
return attachments.mapTo(CancelableBag()) {
|
return attachments.mapTo(CancelableBag()) {
|
||||||
sendMedia(it)
|
sendMedia(it, compressBeforeSending)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -201,18 +201,18 @@ internal class DefaultSendService @AssistedInject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun sendMedia(attachment: ContentAttachmentData): Cancelable {
|
override fun sendMedia(attachment: ContentAttachmentData, compressBeforeSending: Boolean): Cancelable {
|
||||||
// Create an event with the media file path
|
// Create an event with the media file path
|
||||||
val event = localEchoEventFactory.createMediaEvent(roomId, attachment).also {
|
val event = localEchoEventFactory.createMediaEvent(roomId, attachment).also {
|
||||||
createLocalEcho(it)
|
createLocalEcho(it)
|
||||||
}
|
}
|
||||||
return internalSendMedia(event, attachment)
|
return internalSendMedia(event, attachment, compressBeforeSending)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun internalSendMedia(localEcho: Event, attachment: ContentAttachmentData): Cancelable {
|
private fun internalSendMedia(localEcho: Event, attachment: ContentAttachmentData, compressBeforeSending: Boolean): Cancelable {
|
||||||
val isRoomEncrypted = cryptoService.isRoomEncrypted(roomId)
|
val isRoomEncrypted = cryptoService.isRoomEncrypted(roomId)
|
||||||
|
|
||||||
val uploadWork = createUploadMediaWork(localEcho, attachment, isRoomEncrypted, startChain = true)
|
val uploadWork = createUploadMediaWork(localEcho, attachment, isRoomEncrypted, compressBeforeSending, startChain = true)
|
||||||
val sendWork = createSendEventWork(localEcho, false)
|
val sendWork = createSendEventWork(localEcho, false)
|
||||||
|
|
||||||
if (isRoomEncrypted) {
|
if (isRoomEncrypted) {
|
||||||
|
@ -280,8 +280,9 @@ internal class DefaultSendService @AssistedInject constructor(
|
||||||
private fun createUploadMediaWork(event: Event,
|
private fun createUploadMediaWork(event: Event,
|
||||||
attachment: ContentAttachmentData,
|
attachment: ContentAttachmentData,
|
||||||
isRoomEncrypted: Boolean,
|
isRoomEncrypted: Boolean,
|
||||||
|
compressBeforeSending: Boolean,
|
||||||
startChain: Boolean): OneTimeWorkRequest {
|
startChain: Boolean): OneTimeWorkRequest {
|
||||||
val uploadMediaWorkerParams = UploadContentWorker.Params(sessionId, roomId, event, attachment, isRoomEncrypted)
|
val uploadMediaWorkerParams = UploadContentWorker.Params(sessionId, roomId, event, attachment, isRoomEncrypted, compressBeforeSending)
|
||||||
val uploadWorkData = WorkerParamsFactory.toData(uploadMediaWorkerParams)
|
val uploadWorkData = WorkerParamsFactory.toData(uploadMediaWorkerParams)
|
||||||
|
|
||||||
return workManagerProvider.matrixOneTimeWorkRequestBuilder<UploadContentWorker>()
|
return workManagerProvider.matrixOneTimeWorkRequestBuilder<UploadContentWorker>()
|
||||||
|
|
|
@ -20,11 +20,16 @@ import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import com.kbeanie.multipicker.api.Picker.*
|
import com.kbeanie.multipicker.api.Picker.PICK_AUDIO
|
||||||
|
import com.kbeanie.multipicker.api.Picker.PICK_CONTACT
|
||||||
|
import com.kbeanie.multipicker.api.Picker.PICK_FILE
|
||||||
|
import com.kbeanie.multipicker.api.Picker.PICK_IMAGE_CAMERA
|
||||||
|
import com.kbeanie.multipicker.api.Picker.PICK_IMAGE_DEVICE
|
||||||
import com.kbeanie.multipicker.core.PickerManager
|
import com.kbeanie.multipicker.core.PickerManager
|
||||||
import im.vector.matrix.android.BuildConfig
|
import im.vector.matrix.android.BuildConfig
|
||||||
import im.vector.matrix.android.api.session.content.ContentAttachmentData
|
import im.vector.matrix.android.api.session.content.ContentAttachmentData
|
||||||
import im.vector.riotx.core.platform.Restorable
|
import im.vector.riotx.core.platform.Restorable
|
||||||
|
import im.vector.riotx.features.attachments.AttachmentsHelper.Callback
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
|
||||||
private const val CAPTURE_PATH_KEY = "CAPTURE_PATH_KEY"
|
private const val CAPTURE_PATH_KEY = "CAPTURE_PATH_KEY"
|
||||||
|
|
|
@ -16,7 +16,11 @@
|
||||||
|
|
||||||
package im.vector.riotx.features.attachments
|
package im.vector.riotx.features.attachments
|
||||||
|
|
||||||
import com.kbeanie.multipicker.api.entity.*
|
import com.kbeanie.multipicker.api.entity.ChosenAudio
|
||||||
|
import com.kbeanie.multipicker.api.entity.ChosenContact
|
||||||
|
import com.kbeanie.multipicker.api.entity.ChosenFile
|
||||||
|
import com.kbeanie.multipicker.api.entity.ChosenImage
|
||||||
|
import com.kbeanie.multipicker.api.entity.ChosenVideo
|
||||||
import im.vector.matrix.android.api.session.content.ContentAttachmentData
|
import im.vector.matrix.android.api.session.content.ContentAttachmentData
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,11 @@ import com.kbeanie.multipicker.api.callbacks.ContactPickerCallback
|
||||||
import com.kbeanie.multipicker.api.callbacks.FilePickerCallback
|
import com.kbeanie.multipicker.api.callbacks.FilePickerCallback
|
||||||
import com.kbeanie.multipicker.api.callbacks.ImagePickerCallback
|
import com.kbeanie.multipicker.api.callbacks.ImagePickerCallback
|
||||||
import com.kbeanie.multipicker.api.callbacks.VideoPickerCallback
|
import com.kbeanie.multipicker.api.callbacks.VideoPickerCallback
|
||||||
import com.kbeanie.multipicker.api.entity.*
|
import com.kbeanie.multipicker.api.entity.ChosenAudio
|
||||||
|
import com.kbeanie.multipicker.api.entity.ChosenContact
|
||||||
|
import com.kbeanie.multipicker.api.entity.ChosenFile
|
||||||
|
import com.kbeanie.multipicker.api.entity.ChosenImage
|
||||||
|
import com.kbeanie.multipicker.api.entity.ChosenVideo
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class delegates the PickerManager callbacks to an [AttachmentsHelper.Callback]
|
* This class delegates the PickerManager callbacks to an [AttachmentsHelper.Callback]
|
||||||
|
|
|
@ -30,10 +30,11 @@ import im.vector.riotx.features.themes.ActivityOtherThemes
|
||||||
class AttachmentsPreviewActivity : VectorBaseActivity(), ToolbarConfigurable {
|
class AttachmentsPreviewActivity : VectorBaseActivity(), ToolbarConfigurable {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
const val REQUEST_CODE = 55
|
||||||
|
|
||||||
private const val EXTRA_FRAGMENT_ARGS = "EXTRA_FRAGMENT_ARGS"
|
private const val EXTRA_FRAGMENT_ARGS = "EXTRA_FRAGMENT_ARGS"
|
||||||
const val RESULT_NAME = "ATTACHMENTS_PREVIEW_RESULT"
|
private const val ATTACHMENTS_PREVIEW_RESULT = "ATTACHMENTS_PREVIEW_RESULT"
|
||||||
const val REQUEST_CODE = 55
|
private const val KEEP_ORIGINAL_IMAGES_SIZE = "KEEP_ORIGINAL_IMAGES_SIZE"
|
||||||
|
|
||||||
fun newIntent(context: Context, args: AttachmentsPreviewArgs): Intent {
|
fun newIntent(context: Context, args: AttachmentsPreviewArgs): Intent {
|
||||||
return Intent(context, AttachmentsPreviewActivity::class.java).apply {
|
return Intent(context, AttachmentsPreviewActivity::class.java).apply {
|
||||||
|
@ -42,7 +43,11 @@ class AttachmentsPreviewActivity : VectorBaseActivity(), ToolbarConfigurable {
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getOutput(intent: Intent): List<ContentAttachmentData> {
|
fun getOutput(intent: Intent): List<ContentAttachmentData> {
|
||||||
return intent.getParcelableArrayListExtra(RESULT_NAME)
|
return intent.getParcelableArrayListExtra(ATTACHMENTS_PREVIEW_RESULT)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getKeepOriginalSize(intent: Intent): Boolean {
|
||||||
|
return intent.getBooleanExtra(KEEP_ORIGINAL_IMAGES_SIZE, false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -52,12 +57,20 @@ class AttachmentsPreviewActivity : VectorBaseActivity(), ToolbarConfigurable {
|
||||||
|
|
||||||
override fun initUiAndData() {
|
override fun initUiAndData() {
|
||||||
if (isFirstCreation()) {
|
if (isFirstCreation()) {
|
||||||
val fragmentArgs: AttachmentsPreviewArgs = intent?.extras?.getParcelable(EXTRA_FRAGMENT_ARGS)
|
val fragmentArgs: AttachmentsPreviewArgs = intent?.extras?.getParcelable(EXTRA_FRAGMENT_ARGS) ?: return
|
||||||
?: return
|
|
||||||
addFragment(R.id.simpleFragmentContainer, AttachmentsPreviewFragment::class.java, fragmentArgs)
|
addFragment(R.id.simpleFragmentContainer, AttachmentsPreviewFragment::class.java, fragmentArgs)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun setResultAndFinish(data: List<ContentAttachmentData>, keepOriginalImageSize: Boolean) {
|
||||||
|
val resultIntent = Intent().apply {
|
||||||
|
putParcelableArrayListExtra(ATTACHMENTS_PREVIEW_RESULT, ArrayList(data))
|
||||||
|
putExtra(KEEP_ORIGINAL_IMAGES_SIZE, keepOriginalImageSize)
|
||||||
|
}
|
||||||
|
setResult(RESULT_OK, resultIntent)
|
||||||
|
finish()
|
||||||
|
}
|
||||||
|
|
||||||
override fun configure(toolbar: Toolbar) {
|
override fun configure(toolbar: Toolbar) {
|
||||||
configureToolbar(toolbar)
|
configureToolbar(toolbar)
|
||||||
}
|
}
|
||||||
|
|
|
@ -54,7 +54,6 @@ import javax.inject.Inject
|
||||||
|
|
||||||
@Parcelize
|
@Parcelize
|
||||||
data class AttachmentsPreviewArgs(
|
data class AttachmentsPreviewArgs(
|
||||||
val roomId: String,
|
|
||||||
val attachments: List<ContentAttachmentData>
|
val attachments: List<ContentAttachmentData>
|
||||||
) : Parcelable
|
) : Parcelable
|
||||||
|
|
||||||
|
@ -133,11 +132,10 @@ class AttachmentsPreviewFragment @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setResultAndFinish() = withState(viewModel) {
|
private fun setResultAndFinish() = withState(viewModel) {
|
||||||
val resultIntent = Intent().apply {
|
(requireActivity() as? AttachmentsPreviewActivity)?.setResultAndFinish(
|
||||||
putParcelableArrayListExtra(AttachmentsPreviewActivity.RESULT_NAME, ArrayList(it.attachments))
|
it.attachments,
|
||||||
}
|
attachmentPreviewerSendImageOriginalSize.isChecked
|
||||||
requireActivity().setResult(RESULT_OK, resultIntent)
|
)
|
||||||
requireActivity().finish()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun applyInsets() {
|
private fun applyInsets() {
|
||||||
|
|
|
@ -17,7 +17,6 @@
|
||||||
|
|
||||||
package im.vector.riotx.features.attachments.preview
|
package im.vector.riotx.features.attachments.preview
|
||||||
|
|
||||||
import androidx.lifecycle.viewModelScope
|
|
||||||
import com.airbnb.mvrx.FragmentViewModelContext
|
import com.airbnb.mvrx.FragmentViewModelContext
|
||||||
import com.airbnb.mvrx.MvRxViewModelFactory
|
import com.airbnb.mvrx.MvRxViewModelFactory
|
||||||
import com.airbnb.mvrx.ViewModelContext
|
import com.airbnb.mvrx.ViewModelContext
|
||||||
|
@ -25,7 +24,6 @@ import com.squareup.inject.assisted.Assisted
|
||||||
import com.squareup.inject.assisted.AssistedInject
|
import com.squareup.inject.assisted.AssistedInject
|
||||||
import im.vector.riotx.core.extensions.exhaustive
|
import im.vector.riotx.core.extensions.exhaustive
|
||||||
import im.vector.riotx.core.platform.VectorViewModel
|
import im.vector.riotx.core.platform.VectorViewModel
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
|
|
||||||
class AttachmentsPreviewViewModel @AssistedInject constructor(@Assisted initialState: AttachmentsPreviewViewState)
|
class AttachmentsPreviewViewModel @AssistedInject constructor(@Assisted initialState: AttachmentsPreviewViewState)
|
||||||
: VectorViewModel<AttachmentsPreviewViewState, AttachmentsPreviewAction, AttachmentsPreviewViewEvents>(initialState) {
|
: VectorViewModel<AttachmentsPreviewViewState, AttachmentsPreviewAction, AttachmentsPreviewViewEvents>(initialState) {
|
||||||
|
|
|
@ -22,7 +22,8 @@ import im.vector.matrix.android.api.session.content.ContentAttachmentData
|
||||||
|
|
||||||
data class AttachmentsPreviewViewState(
|
data class AttachmentsPreviewViewState(
|
||||||
val attachments: List<ContentAttachmentData>,
|
val attachments: List<ContentAttachmentData>,
|
||||||
val currentAttachmentIndex: Int = 0
|
val currentAttachmentIndex: Int = 0,
|
||||||
|
val sendImagesWithOriginalSize: Boolean = false
|
||||||
) : MvRxState {
|
) : MvRxState {
|
||||||
|
|
||||||
constructor(args: AttachmentsPreviewArgs) : this(attachments = args.attachments)
|
constructor(args: AttachmentsPreviewArgs) : this(attachments = args.attachments)
|
||||||
|
|
|
@ -27,7 +27,7 @@ sealed class RoomDetailAction : VectorViewModelAction {
|
||||||
data class UserIsTyping(val isTyping: Boolean) : RoomDetailAction()
|
data class UserIsTyping(val isTyping: Boolean) : RoomDetailAction()
|
||||||
data class SaveDraft(val draft: String) : RoomDetailAction()
|
data class SaveDraft(val draft: String) : RoomDetailAction()
|
||||||
data class SendMessage(val text: CharSequence, val autoMarkdown: Boolean) : RoomDetailAction()
|
data class SendMessage(val text: CharSequence, val autoMarkdown: Boolean) : RoomDetailAction()
|
||||||
data class SendMedia(val attachments: List<ContentAttachmentData>) : RoomDetailAction()
|
data class SendMedia(val attachments: List<ContentAttachmentData>, val compressBeforeSending: Boolean) : RoomDetailAction()
|
||||||
data class TimelineEventTurnsVisible(val event: TimelineEvent) : RoomDetailAction()
|
data class TimelineEventTurnsVisible(val event: TimelineEvent) : RoomDetailAction()
|
||||||
data class TimelineEventTurnsInvisible(val event: TimelineEvent) : RoomDetailAction()
|
data class TimelineEventTurnsInvisible(val event: TimelineEvent) : RoomDetailAction()
|
||||||
data class LoadMoreTimelineEvents(val direction: Timeline.Direction) : RoomDetailAction()
|
data class LoadMoreTimelineEvents(val direction: Timeline.Direction) : RoomDetailAction()
|
||||||
|
|
|
@ -156,7 +156,6 @@ import io.reactivex.android.schedulers.AndroidSchedulers
|
||||||
import io.reactivex.schedulers.Schedulers
|
import io.reactivex.schedulers.Schedulers
|
||||||
import kotlinx.android.parcel.Parcelize
|
import kotlinx.android.parcel.Parcelize
|
||||||
import kotlinx.android.synthetic.main.fragment_room_detail.*
|
import kotlinx.android.synthetic.main.fragment_room_detail.*
|
||||||
import kotlinx.android.synthetic.main.merge_composer_layout.*
|
|
||||||
import kotlinx.android.synthetic.main.merge_composer_layout.view.*
|
import kotlinx.android.synthetic.main.merge_composer_layout.view.*
|
||||||
import kotlinx.android.synthetic.main.merge_overlay_waiting_view.*
|
import kotlinx.android.synthetic.main.merge_overlay_waiting_view.*
|
||||||
import org.commonmark.parser.Parser
|
import org.commonmark.parser.Parser
|
||||||
|
@ -306,7 +305,10 @@ class RoomDetailFragment @Inject constructor(
|
||||||
is SharedData.Text -> {
|
is SharedData.Text -> {
|
||||||
roomDetailViewModel.handle(RoomDetailAction.ExitSpecialMode(composerLayout.text.toString()))
|
roomDetailViewModel.handle(RoomDetailAction.ExitSpecialMode(composerLayout.text.toString()))
|
||||||
}
|
}
|
||||||
is SharedData.Attachments -> roomDetailViewModel.handle(RoomDetailAction.SendMedia(sharedData.attachmentData))
|
is SharedData.Attachments -> {
|
||||||
|
// open share edition
|
||||||
|
onContentAttachmentsReady(sharedData.attachmentData)
|
||||||
|
}
|
||||||
null -> Timber.v("No share data to process")
|
null -> Timber.v("No share data to process")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -506,7 +508,8 @@ class RoomDetailFragment @Inject constructor(
|
||||||
when (requestCode) {
|
when (requestCode) {
|
||||||
AttachmentsPreviewActivity.REQUEST_CODE -> {
|
AttachmentsPreviewActivity.REQUEST_CODE -> {
|
||||||
val sendData = AttachmentsPreviewActivity.getOutput(data)
|
val sendData = AttachmentsPreviewActivity.getOutput(data)
|
||||||
roomDetailViewModel.handle(RoomDetailAction.SendMedia(sendData))
|
val keepOriginalSize = AttachmentsPreviewActivity.getKeepOriginalSize(data)
|
||||||
|
roomDetailViewModel.handle(RoomDetailAction.SendMedia(sendData, keepOriginalSize))
|
||||||
}
|
}
|
||||||
REACTION_SELECT_REQUEST_CODE -> {
|
REACTION_SELECT_REQUEST_CODE -> {
|
||||||
val (eventId, reaction) = EmojiReactionPickerActivity.getOutput(data) ?: return
|
val (eventId, reaction) = EmojiReactionPickerActivity.getOutput(data) ?: return
|
||||||
|
@ -1351,11 +1354,11 @@ class RoomDetailFragment @Inject constructor(
|
||||||
val previewable = attachments.filterPreviewables()
|
val previewable = attachments.filterPreviewables()
|
||||||
val nonPreviewable = attachments.filterNonPreviewables()
|
val nonPreviewable = attachments.filterNonPreviewables()
|
||||||
if (nonPreviewable.isNotEmpty()) {
|
if (nonPreviewable.isNotEmpty()) {
|
||||||
// Send the non previewable event right now (?)
|
// Send the non previewable attachment right now (?)
|
||||||
roomDetailViewModel.handle(RoomDetailAction.SendMedia(nonPreviewable))
|
roomDetailViewModel.handle(RoomDetailAction.SendMedia(nonPreviewable, false))
|
||||||
}
|
}
|
||||||
if (previewable.isNotEmpty()) {
|
if (previewable.isNotEmpty()) {
|
||||||
val intent = AttachmentsPreviewActivity.newIntent(requireContext(), AttachmentsPreviewArgs(roomDetailArgs.roomId, previewable))
|
val intent = AttachmentsPreviewActivity.newIntent(requireContext(), AttachmentsPreviewArgs(previewable))
|
||||||
startActivityForResult(intent, AttachmentsPreviewActivity.REQUEST_CODE)
|
startActivityForResult(intent, AttachmentsPreviewActivity.REQUEST_CODE)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -579,10 +579,10 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro
|
||||||
|
|
||||||
if (maxUploadFileSize == HomeServerCapabilities.MAX_UPLOAD_FILE_SIZE_UNKNOWN) {
|
if (maxUploadFileSize == HomeServerCapabilities.MAX_UPLOAD_FILE_SIZE_UNKNOWN) {
|
||||||
// Unknown limitation
|
// Unknown limitation
|
||||||
room.sendMedias(attachments)
|
room.sendMedias(attachments, action.compressBeforeSending)
|
||||||
} else {
|
} else {
|
||||||
when (val tooBigFile = attachments.find { it.size > maxUploadFileSize }) {
|
when (val tooBigFile = attachments.find { it.size > maxUploadFileSize }) {
|
||||||
null -> room.sendMedias(attachments)
|
null -> room.sendMedias(attachments, action.compressBeforeSending)
|
||||||
else -> _viewEvents.post(RoomDetailViewEvents.FileTooBigError(
|
else -> _viewEvents.post(RoomDetailViewEvents.FileTooBigError(
|
||||||
tooBigFile.name ?: tooBigFile.path,
|
tooBigFile.name ?: tooBigFile.path,
|
||||||
tooBigFile.size,
|
tooBigFile.size,
|
||||||
|
|
|
@ -22,6 +22,7 @@ import im.vector.riotx.core.platform.VectorViewModelAction
|
||||||
sealed class IncomingShareAction : VectorViewModelAction {
|
sealed class IncomingShareAction : VectorViewModelAction {
|
||||||
data class SelectRoom(val roomSummary: RoomSummary, val enableMultiSelect: Boolean) : IncomingShareAction()
|
data class SelectRoom(val roomSummary: RoomSummary, val enableMultiSelect: Boolean) : IncomingShareAction()
|
||||||
object ShareToSelectedRooms : IncomingShareAction()
|
object ShareToSelectedRooms : IncomingShareAction()
|
||||||
|
data class ShareMedia(val keepOriginalSize: Boolean) : IncomingShareAction()
|
||||||
data class FilterWith(val filter: String) : IncomingShareAction()
|
data class FilterWith(val filter: String) : IncomingShareAction()
|
||||||
data class UpdateSharedData(val sharedData: SharedData) : IncomingShareAction()
|
data class UpdateSharedData(val sharedData: SharedData) : IncomingShareAction()
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
|
|
||||||
package im.vector.riotx.features.share
|
package im.vector.riotx.features.share
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
import android.content.ClipDescription
|
import android.content.ClipDescription
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
@ -41,6 +42,8 @@ import im.vector.riotx.core.utils.PERMISSION_REQUEST_CODE_PICK_ATTACHMENT
|
||||||
import im.vector.riotx.core.utils.allGranted
|
import im.vector.riotx.core.utils.allGranted
|
||||||
import im.vector.riotx.core.utils.checkPermissions
|
import im.vector.riotx.core.utils.checkPermissions
|
||||||
import im.vector.riotx.features.attachments.AttachmentsHelper
|
import im.vector.riotx.features.attachments.AttachmentsHelper
|
||||||
|
import im.vector.riotx.features.attachments.preview.AttachmentsPreviewActivity
|
||||||
|
import im.vector.riotx.features.attachments.preview.AttachmentsPreviewArgs
|
||||||
import im.vector.riotx.features.login.LoginActivity
|
import im.vector.riotx.features.login.LoginActivity
|
||||||
import kotlinx.android.synthetic.main.fragment_incoming_share.*
|
import kotlinx.android.synthetic.main.fragment_incoming_share.*
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
@ -99,10 +102,30 @@ class IncomingShareFragment @Inject constructor(
|
||||||
incomingShareViewModel.observeViewEvents {
|
incomingShareViewModel.observeViewEvents {
|
||||||
when (it) {
|
when (it) {
|
||||||
is IncomingShareViewEvents.ShareToRoom -> handleShareToRoom(it)
|
is IncomingShareViewEvents.ShareToRoom -> handleShareToRoom(it)
|
||||||
|
is IncomingShareViewEvents.EditMediaBeforeSending -> handleEditMediaBeforeSending(it)
|
||||||
}.exhaustive
|
}.exhaustive
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun handleEditMediaBeforeSending(event: IncomingShareViewEvents.EditMediaBeforeSending) {
|
||||||
|
val intent = AttachmentsPreviewActivity.newIntent(requireContext(), AttachmentsPreviewArgs(event.contentAttachmentData))
|
||||||
|
startActivityForResult(intent, AttachmentsPreviewActivity.REQUEST_CODE)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||||
|
val hasBeenHandled = attachmentsHelper.onActivityResult(requestCode, resultCode, data)
|
||||||
|
if (!hasBeenHandled && resultCode == Activity.RESULT_OK && data != null) {
|
||||||
|
when (requestCode) {
|
||||||
|
AttachmentsPreviewActivity.REQUEST_CODE -> {
|
||||||
|
val sendData = AttachmentsPreviewActivity.getOutput(data)
|
||||||
|
val keepOriginalSize = AttachmentsPreviewActivity.getKeepOriginalSize(data)
|
||||||
|
incomingShareViewModel.handle(IncomingShareAction.UpdateSharedData(SharedData.Attachments(sendData)))
|
||||||
|
incomingShareViewModel.handle(IncomingShareAction.ShareMedia(keepOriginalSize))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun onResume() {
|
override fun onResume() {
|
||||||
super.onResume()
|
super.onResume()
|
||||||
|
|
||||||
|
@ -189,7 +212,7 @@ class IncomingShareFragment @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun invalidate() = withState(incomingShareViewModel) {
|
override fun invalidate() = withState(incomingShareViewModel) {
|
||||||
sendShareButton.isVisible = it.multiSelectionEnabled
|
sendShareButton.isVisible = it.isInMultiSelectionMode
|
||||||
incomingShareController.setData(it)
|
incomingShareController.setData(it)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,9 +16,11 @@
|
||||||
|
|
||||||
package im.vector.riotx.features.share
|
package im.vector.riotx.features.share
|
||||||
|
|
||||||
|
import im.vector.matrix.android.api.session.content.ContentAttachmentData
|
||||||
import im.vector.matrix.android.api.session.room.model.RoomSummary
|
import im.vector.matrix.android.api.session.room.model.RoomSummary
|
||||||
import im.vector.riotx.core.platform.VectorViewEvents
|
import im.vector.riotx.core.platform.VectorViewEvents
|
||||||
|
|
||||||
sealed class IncomingShareViewEvents : VectorViewEvents {
|
sealed class IncomingShareViewEvents : VectorViewEvents {
|
||||||
data class ShareToRoom(val roomSummary: RoomSummary, val sharedData: SharedData, val showAlert: Boolean) : IncomingShareViewEvents()
|
data class ShareToRoom(val roomSummary: RoomSummary, val sharedData: SharedData, val showAlert: Boolean) : IncomingShareViewEvents()
|
||||||
|
data class EditMediaBeforeSending(val contentAttachmentData: List<ContentAttachmentData>) : IncomingShareViewEvents()
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,12 +24,14 @@ import com.squareup.inject.assisted.Assisted
|
||||||
import com.squareup.inject.assisted.AssistedInject
|
import com.squareup.inject.assisted.AssistedInject
|
||||||
import im.vector.matrix.android.api.query.QueryStringValue
|
import im.vector.matrix.android.api.query.QueryStringValue
|
||||||
import im.vector.matrix.android.api.session.Session
|
import im.vector.matrix.android.api.session.Session
|
||||||
|
import im.vector.matrix.android.api.session.content.ContentAttachmentData
|
||||||
import im.vector.matrix.android.api.session.room.model.Membership
|
import im.vector.matrix.android.api.session.room.model.Membership
|
||||||
import im.vector.matrix.android.api.session.room.roomSummaryQueryParams
|
import im.vector.matrix.android.api.session.room.roomSummaryQueryParams
|
||||||
import im.vector.matrix.rx.rx
|
import im.vector.matrix.rx.rx
|
||||||
import im.vector.riotx.core.extensions.exhaustive
|
import im.vector.riotx.core.extensions.exhaustive
|
||||||
import im.vector.riotx.core.platform.VectorViewModel
|
import im.vector.riotx.core.platform.VectorViewModel
|
||||||
import im.vector.riotx.features.home.room.list.BreadcrumbsRoomComparator
|
import im.vector.riotx.features.attachments.filterNonPreviewables
|
||||||
|
import im.vector.riotx.features.attachments.filterPreviewables
|
||||||
import im.vector.riotx.features.home.room.list.ChronologicalRoomComparator
|
import im.vector.riotx.features.home.room.list.ChronologicalRoomComparator
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
|
@ -92,6 +94,7 @@ class IncomingShareViewModel @AssistedInject constructor(@Assisted initialState:
|
||||||
when (action) {
|
when (action) {
|
||||||
is IncomingShareAction.SelectRoom -> handleSelectRoom(action)
|
is IncomingShareAction.SelectRoom -> handleSelectRoom(action)
|
||||||
is IncomingShareAction.ShareToSelectedRooms -> handleShareToSelectedRooms()
|
is IncomingShareAction.ShareToSelectedRooms -> handleShareToSelectedRooms()
|
||||||
|
is IncomingShareAction.ShareMedia -> handleShareMediaToSelectedRooms(action)
|
||||||
is IncomingShareAction.FilterWith -> handleFilter(action)
|
is IncomingShareAction.FilterWith -> handleFilter(action)
|
||||||
is IncomingShareAction.UpdateSharedData -> handleUpdateSharedData(action)
|
is IncomingShareAction.UpdateSharedData -> handleUpdateSharedData(action)
|
||||||
}.exhaustive
|
}.exhaustive
|
||||||
|
@ -108,34 +111,70 @@ class IncomingShareViewModel @AssistedInject constructor(@Assisted initialState:
|
||||||
private fun handleShareToSelectedRooms() = withState { state ->
|
private fun handleShareToSelectedRooms() = withState { state ->
|
||||||
val sharedData = state.sharedData ?: return@withState
|
val sharedData = state.sharedData ?: return@withState
|
||||||
if (state.selectedRoomIds.size == 1) {
|
if (state.selectedRoomIds.size == 1) {
|
||||||
|
// In this case the edition of the media will be handled by the RoomDetailFragment
|
||||||
val selectedRoomId = state.selectedRoomIds.first()
|
val selectedRoomId = state.selectedRoomIds.first()
|
||||||
val selectedRoom = state.roomSummaries()?.find { it.roomId == selectedRoomId } ?: return@withState
|
val selectedRoom = state.roomSummaries()?.find { it.roomId == selectedRoomId } ?: return@withState
|
||||||
_viewEvents.post(IncomingShareViewEvents.ShareToRoom(selectedRoom, sharedData, showAlert = false))
|
_viewEvents.post(IncomingShareViewEvents.ShareToRoom(selectedRoom, sharedData, showAlert = false))
|
||||||
} else {
|
} else {
|
||||||
|
when (sharedData) {
|
||||||
|
is SharedData.Text -> {
|
||||||
state.selectedRoomIds.forEach { roomId ->
|
state.selectedRoomIds.forEach { roomId ->
|
||||||
val room = session.getRoom(roomId)
|
val room = session.getRoom(roomId)
|
||||||
if (sharedData is SharedData.Text) {
|
|
||||||
room?.sendTextMessage(sharedData.text)
|
room?.sendTextMessage(sharedData.text)
|
||||||
} else if (sharedData is SharedData.Attachments) {
|
}
|
||||||
room?.sendMedias(sharedData.attachmentData)
|
}
|
||||||
|
is SharedData.Attachments -> {
|
||||||
|
shareAttachments(sharedData.attachmentData, state.selectedRoomIds, proposeMediaEdition = true, compressMediaBeforeSending = false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleSelectRoom(action: IncomingShareAction.SelectRoom) = withState {
|
private fun shareAttachments(attachmentData: List<ContentAttachmentData>,
|
||||||
if (it.multiSelectionEnabled) {
|
selectedRoomIds: Set<String>,
|
||||||
val selectedRooms = it.selectedRoomIds
|
proposeMediaEdition: Boolean,
|
||||||
|
compressMediaBeforeSending: Boolean) {
|
||||||
|
if (!proposeMediaEdition) {
|
||||||
|
selectedRoomIds.forEach { roomId ->
|
||||||
|
val room = session.getRoom(roomId)
|
||||||
|
room?.sendMedias(attachmentData, compressMediaBeforeSending)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
val previewable = attachmentData.filterPreviewables()
|
||||||
|
val nonPreviewable = attachmentData.filterNonPreviewables()
|
||||||
|
if (nonPreviewable.isNotEmpty()) {
|
||||||
|
// Send the non previewable attachment right now (?)
|
||||||
|
selectedRoomIds.forEach { roomId ->
|
||||||
|
val room = session.getRoom(roomId)
|
||||||
|
room?.sendMedias(nonPreviewable, compressMediaBeforeSending)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (previewable.isNotEmpty()) {
|
||||||
|
// In case of multiple share of media, edit them first
|
||||||
|
_viewEvents.post(IncomingShareViewEvents.EditMediaBeforeSending(previewable))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun handleShareMediaToSelectedRooms(action: IncomingShareAction.ShareMedia) = withState { state ->
|
||||||
|
(state.sharedData as? SharedData.Attachments)?.let {
|
||||||
|
shareAttachments(it.attachmentData, state.selectedRoomIds, proposeMediaEdition = false, compressMediaBeforeSending = !action.keepOriginalSize)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun handleSelectRoom(action: IncomingShareAction.SelectRoom) = withState { state ->
|
||||||
|
if (state.isInMultiSelectionMode) {
|
||||||
|
val selectedRooms = state.selectedRoomIds
|
||||||
val newSelectedRooms = if (selectedRooms.contains(action.roomSummary.roomId)) {
|
val newSelectedRooms = if (selectedRooms.contains(action.roomSummary.roomId)) {
|
||||||
selectedRooms.minus(action.roomSummary.roomId)
|
selectedRooms.minus(action.roomSummary.roomId)
|
||||||
} else {
|
} else {
|
||||||
selectedRooms.plus(action.roomSummary.roomId)
|
selectedRooms.plus(action.roomSummary.roomId)
|
||||||
}
|
}
|
||||||
setState { copy(multiSelectionEnabled = newSelectedRooms.isNotEmpty(), selectedRoomIds = newSelectedRooms) }
|
setState { copy(isInMultiSelectionMode = newSelectedRooms.isNotEmpty(), selectedRoomIds = newSelectedRooms) }
|
||||||
} else if (action.enableMultiSelect) {
|
} else if (action.enableMultiSelect) {
|
||||||
setState { copy(multiSelectionEnabled = true, selectedRoomIds = setOf(action.roomSummary.roomId)) }
|
setState { copy(isInMultiSelectionMode = true, selectedRoomIds = setOf(action.roomSummary.roomId)) }
|
||||||
} else {
|
} else {
|
||||||
val sharedData = it.sharedData ?: return@withState
|
val sharedData = state.sharedData ?: return@withState
|
||||||
_viewEvents.post(IncomingShareViewEvents.ShareToRoom(action.roomSummary, sharedData, showAlert = true))
|
_viewEvents.post(IncomingShareViewEvents.ShareToRoom(action.roomSummary, sharedData, showAlert = true))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,7 +26,7 @@ data class IncomingShareViewState(
|
||||||
val roomSummaries: Async<List<RoomSummary>> = Uninitialized,
|
val roomSummaries: Async<List<RoomSummary>> = Uninitialized,
|
||||||
val filteredRoomSummaries: Async<List<RoomSummary>> = Uninitialized,
|
val filteredRoomSummaries: Async<List<RoomSummary>> = Uninitialized,
|
||||||
val selectedRoomIds: Set<String> = emptySet(),
|
val selectedRoomIds: Set<String> = emptySet(),
|
||||||
val multiSelectionEnabled: Boolean = false
|
val isInMultiSelectionMode: Boolean = false
|
||||||
) : MvRxState
|
) : MvRxState
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -39,11 +39,24 @@
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:padding="8dp"
|
android:padding="8dp"
|
||||||
|
app:layout_constraintBottom_toTopOf="@+id/attachmentPreviewerSendImageOriginalSize"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
tools:itemCount="1"
|
||||||
|
tools:listitem="@layout/item_attachment_miniature_preview" />
|
||||||
|
|
||||||
|
<com.google.android.material.checkbox.MaterialCheckBox
|
||||||
|
android:id="@+id/attachmentPreviewerSendImageOriginalSize"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:padding="8dp"
|
||||||
|
android:textColor="@color/white"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
tools:itemCount="1"
|
app:layout_constraintTop_toBottomOf="@+id/attachmentPreviewerMiniatureList"
|
||||||
tools:listitem="@layout/item_attachment_miniature_preview" />
|
tools:text="@plurals/send_images_with_original_size" />
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
||||||
|
|
|
@ -22,6 +22,10 @@
|
||||||
<!-- BEGIN Strings added by Benoit -->
|
<!-- BEGIN Strings added by Benoit -->
|
||||||
<string name="message_action_item_redact">Remove…</string>
|
<string name="message_action_item_redact">Remove…</string>
|
||||||
<string name="share_confirm_room">Do you want to send this attachment to %1$s?</string>
|
<string name="share_confirm_room">Do you want to send this attachment to %1$s?</string>
|
||||||
|
<plurals name="send_images_with_original_size">
|
||||||
|
<item quantity="one">Send image with the original size</item>
|
||||||
|
<item quantity="other">Send images with the original size</item>
|
||||||
|
</plurals>
|
||||||
<!-- END Strings added by Benoit -->
|
<!-- END Strings added by Benoit -->
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue