diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/send/SendService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/send/SendService.kt index e6c32193f4..b61f162359 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/send/SendService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/send/SendService.kt @@ -51,16 +51,21 @@ interface SendService { /** * Method to send a media asynchronously. * @param attachment the media to send + * @param compressBeforeSending set to true to compress media before sending them * @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. * @param attachments the list of media to send * @return a [Cancelable] */ - fun sendMedias(attachments: List): Cancelable + fun sendMedias(attachments: List, + // TODO Change to a Compression Level Enum + compressBeforeSending: Boolean): Cancelable /** * Send a poll to the room. diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/content/UploadContentWorker.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/content/UploadContentWorker.kt index 1725ef99aa..a7fa68f96f 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/content/UploadContentWorker.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/content/UploadContentWorker.kt @@ -47,6 +47,7 @@ internal class UploadContentWorker(context: Context, params: WorkerParameters) : val event: Event, val attachment: ContentAttachmentData, val isRoomEncrypted: Boolean, + val compressBeforeSending: Boolean, override val lastFailureMessage: String? = null ) : SessionWorkerParams @@ -82,6 +83,8 @@ internal class UploadContentWorker(context: Context, params: WorkerParameters) : ) } + // TODO Use compressBeforeSending + var uploadedThumbnailUrl: String? = null var uploadedThumbnailEncryptedFileInfo: EncryptedFileInfo? = null diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/DefaultSendService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/DefaultSendService.kt index fbb80adf83..da8ac7f2f7 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/DefaultSendService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/DefaultSendService.kt @@ -111,9 +111,9 @@ internal class DefaultSendService @AssistedInject constructor( } } - override fun sendMedias(attachments: List): Cancelable { + override fun sendMedias(attachments: List, compressBeforeSending: Boolean): Cancelable { 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 val event = localEchoEventFactory.createMediaEvent(roomId, attachment).also { 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 uploadWork = createUploadMediaWork(localEcho, attachment, isRoomEncrypted, startChain = true) + val uploadWork = createUploadMediaWork(localEcho, attachment, isRoomEncrypted, compressBeforeSending, startChain = true) val sendWork = createSendEventWork(localEcho, false) if (isRoomEncrypted) { @@ -280,8 +280,9 @@ internal class DefaultSendService @AssistedInject constructor( private fun createUploadMediaWork(event: Event, attachment: ContentAttachmentData, isRoomEncrypted: Boolean, + compressBeforeSending: Boolean, 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) return workManagerProvider.matrixOneTimeWorkRequestBuilder() diff --git a/vector/src/main/java/im/vector/riotx/features/attachments/AttachmentsHelper.kt b/vector/src/main/java/im/vector/riotx/features/attachments/AttachmentsHelper.kt index 8a4a0d9309..8eb7a5bb52 100644 --- a/vector/src/main/java/im/vector/riotx/features/attachments/AttachmentsHelper.kt +++ b/vector/src/main/java/im/vector/riotx/features/attachments/AttachmentsHelper.kt @@ -20,11 +20,16 @@ import android.content.Context import android.content.Intent import android.os.Bundle 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 im.vector.matrix.android.BuildConfig import im.vector.matrix.android.api.session.content.ContentAttachmentData import im.vector.riotx.core.platform.Restorable +import im.vector.riotx.features.attachments.AttachmentsHelper.Callback import timber.log.Timber private const val CAPTURE_PATH_KEY = "CAPTURE_PATH_KEY" diff --git a/vector/src/main/java/im/vector/riotx/features/attachments/AttachmentsMapper.kt b/vector/src/main/java/im/vector/riotx/features/attachments/AttachmentsMapper.kt index c0009a7e49..a3de5084de 100644 --- a/vector/src/main/java/im/vector/riotx/features/attachments/AttachmentsMapper.kt +++ b/vector/src/main/java/im/vector/riotx/features/attachments/AttachmentsMapper.kt @@ -16,7 +16,11 @@ 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 timber.log.Timber diff --git a/vector/src/main/java/im/vector/riotx/features/attachments/AttachmentsPickerCallback.kt b/vector/src/main/java/im/vector/riotx/features/attachments/AttachmentsPickerCallback.kt index dc7b028aba..62956e08c8 100644 --- a/vector/src/main/java/im/vector/riotx/features/attachments/AttachmentsPickerCallback.kt +++ b/vector/src/main/java/im/vector/riotx/features/attachments/AttachmentsPickerCallback.kt @@ -21,7 +21,11 @@ import com.kbeanie.multipicker.api.callbacks.ContactPickerCallback import com.kbeanie.multipicker.api.callbacks.FilePickerCallback import com.kbeanie.multipicker.api.callbacks.ImagePickerCallback 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] diff --git a/vector/src/main/java/im/vector/riotx/features/attachments/preview/AttachmentsPreviewActivity.kt b/vector/src/main/java/im/vector/riotx/features/attachments/preview/AttachmentsPreviewActivity.kt index 70dc0734ed..46a90803ca 100644 --- a/vector/src/main/java/im/vector/riotx/features/attachments/preview/AttachmentsPreviewActivity.kt +++ b/vector/src/main/java/im/vector/riotx/features/attachments/preview/AttachmentsPreviewActivity.kt @@ -30,10 +30,11 @@ import im.vector.riotx.features.themes.ActivityOtherThemes class AttachmentsPreviewActivity : VectorBaseActivity(), ToolbarConfigurable { companion object { + const val REQUEST_CODE = 55 private const val EXTRA_FRAGMENT_ARGS = "EXTRA_FRAGMENT_ARGS" - const val RESULT_NAME = "ATTACHMENTS_PREVIEW_RESULT" - const val REQUEST_CODE = 55 + private const val ATTACHMENTS_PREVIEW_RESULT = "ATTACHMENTS_PREVIEW_RESULT" + private const val KEEP_ORIGINAL_IMAGES_SIZE = "KEEP_ORIGINAL_IMAGES_SIZE" fun newIntent(context: Context, args: AttachmentsPreviewArgs): Intent { return Intent(context, AttachmentsPreviewActivity::class.java).apply { @@ -42,7 +43,11 @@ class AttachmentsPreviewActivity : VectorBaseActivity(), ToolbarConfigurable { } fun getOutput(intent: Intent): List { - 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() { if (isFirstCreation()) { - val fragmentArgs: AttachmentsPreviewArgs = intent?.extras?.getParcelable(EXTRA_FRAGMENT_ARGS) - ?: return + val fragmentArgs: AttachmentsPreviewArgs = intent?.extras?.getParcelable(EXTRA_FRAGMENT_ARGS) ?: return addFragment(R.id.simpleFragmentContainer, AttachmentsPreviewFragment::class.java, fragmentArgs) } } + fun setResultAndFinish(data: List, 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) { configureToolbar(toolbar) } diff --git a/vector/src/main/java/im/vector/riotx/features/attachments/preview/AttachmentsPreviewFragment.kt b/vector/src/main/java/im/vector/riotx/features/attachments/preview/AttachmentsPreviewFragment.kt index c9dc9d805d..cb55d076ce 100644 --- a/vector/src/main/java/im/vector/riotx/features/attachments/preview/AttachmentsPreviewFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/attachments/preview/AttachmentsPreviewFragment.kt @@ -54,7 +54,6 @@ import javax.inject.Inject @Parcelize data class AttachmentsPreviewArgs( - val roomId: String, val attachments: List ) : Parcelable @@ -133,11 +132,10 @@ class AttachmentsPreviewFragment @Inject constructor( } private fun setResultAndFinish() = withState(viewModel) { - val resultIntent = Intent().apply { - putParcelableArrayListExtra(AttachmentsPreviewActivity.RESULT_NAME, ArrayList(it.attachments)) - } - requireActivity().setResult(RESULT_OK, resultIntent) - requireActivity().finish() + (requireActivity() as? AttachmentsPreviewActivity)?.setResultAndFinish( + it.attachments, + attachmentPreviewerSendImageOriginalSize.isChecked + ) } private fun applyInsets() { diff --git a/vector/src/main/java/im/vector/riotx/features/attachments/preview/AttachmentsPreviewViewModel.kt b/vector/src/main/java/im/vector/riotx/features/attachments/preview/AttachmentsPreviewViewModel.kt index b3be853eea..1f6c8c2f8b 100644 --- a/vector/src/main/java/im/vector/riotx/features/attachments/preview/AttachmentsPreviewViewModel.kt +++ b/vector/src/main/java/im/vector/riotx/features/attachments/preview/AttachmentsPreviewViewModel.kt @@ -17,7 +17,6 @@ package im.vector.riotx.features.attachments.preview -import androidx.lifecycle.viewModelScope import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.MvRxViewModelFactory import com.airbnb.mvrx.ViewModelContext @@ -25,7 +24,6 @@ import com.squareup.inject.assisted.Assisted import com.squareup.inject.assisted.AssistedInject import im.vector.riotx.core.extensions.exhaustive import im.vector.riotx.core.platform.VectorViewModel -import kotlinx.coroutines.launch class AttachmentsPreviewViewModel @AssistedInject constructor(@Assisted initialState: AttachmentsPreviewViewState) : VectorViewModel(initialState) { diff --git a/vector/src/main/java/im/vector/riotx/features/attachments/preview/AttachmentsPreviewViewState.kt b/vector/src/main/java/im/vector/riotx/features/attachments/preview/AttachmentsPreviewViewState.kt index feb1a32210..50214fd9d7 100644 --- a/vector/src/main/java/im/vector/riotx/features/attachments/preview/AttachmentsPreviewViewState.kt +++ b/vector/src/main/java/im/vector/riotx/features/attachments/preview/AttachmentsPreviewViewState.kt @@ -22,7 +22,8 @@ import im.vector.matrix.android.api.session.content.ContentAttachmentData data class AttachmentsPreviewViewState( val attachments: List, - val currentAttachmentIndex: Int = 0 + val currentAttachmentIndex: Int = 0, + val sendImagesWithOriginalSize: Boolean = false ) : MvRxState { constructor(args: AttachmentsPreviewArgs) : this(attachments = args.attachments) diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailAction.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailAction.kt index 3061c5c9e2..2ef7b11b0e 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailAction.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailAction.kt @@ -27,7 +27,7 @@ sealed class RoomDetailAction : VectorViewModelAction { data class UserIsTyping(val isTyping: Boolean) : RoomDetailAction() data class SaveDraft(val draft: String) : RoomDetailAction() data class SendMessage(val text: CharSequence, val autoMarkdown: Boolean) : RoomDetailAction() - data class SendMedia(val attachments: List) : RoomDetailAction() + data class SendMedia(val attachments: List, val compressBeforeSending: Boolean) : RoomDetailAction() data class TimelineEventTurnsVisible(val event: TimelineEvent) : RoomDetailAction() data class TimelineEventTurnsInvisible(val event: TimelineEvent) : RoomDetailAction() data class LoadMoreTimelineEvents(val direction: Timeline.Direction) : RoomDetailAction() diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailFragment.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailFragment.kt index fbcdcb47fb..ffd0417d05 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailFragment.kt @@ -156,7 +156,6 @@ import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.schedulers.Schedulers import kotlinx.android.parcel.Parcelize 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_overlay_waiting_view.* import org.commonmark.parser.Parser @@ -306,7 +305,10 @@ class RoomDetailFragment @Inject constructor( is SharedData.Text -> { 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") } } @@ -506,7 +508,8 @@ class RoomDetailFragment @Inject constructor( when (requestCode) { AttachmentsPreviewActivity.REQUEST_CODE -> { 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 -> { val (eventId, reaction) = EmojiReactionPickerActivity.getOutput(data) ?: return @@ -1351,11 +1354,11 @@ class RoomDetailFragment @Inject constructor( val previewable = attachments.filterPreviewables() val nonPreviewable = attachments.filterNonPreviewables() if (nonPreviewable.isNotEmpty()) { - // Send the non previewable event right now (?) - roomDetailViewModel.handle(RoomDetailAction.SendMedia(nonPreviewable)) + // Send the non previewable attachment right now (?) + roomDetailViewModel.handle(RoomDetailAction.SendMedia(nonPreviewable, false)) } if (previewable.isNotEmpty()) { - val intent = AttachmentsPreviewActivity.newIntent(requireContext(), AttachmentsPreviewArgs(roomDetailArgs.roomId, previewable)) + val intent = AttachmentsPreviewActivity.newIntent(requireContext(), AttachmentsPreviewArgs(previewable)) startActivityForResult(intent, AttachmentsPreviewActivity.REQUEST_CODE) } } diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailViewModel.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailViewModel.kt index 2ca372707f..a2cb005858 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailViewModel.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailViewModel.kt @@ -579,10 +579,10 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro if (maxUploadFileSize == HomeServerCapabilities.MAX_UPLOAD_FILE_SIZE_UNKNOWN) { // Unknown limitation - room.sendMedias(attachments) + room.sendMedias(attachments, action.compressBeforeSending) } else { when (val tooBigFile = attachments.find { it.size > maxUploadFileSize }) { - null -> room.sendMedias(attachments) + null -> room.sendMedias(attachments, action.compressBeforeSending) else -> _viewEvents.post(RoomDetailViewEvents.FileTooBigError( tooBigFile.name ?: tooBigFile.path, tooBigFile.size, diff --git a/vector/src/main/java/im/vector/riotx/features/share/IncomingShareAction.kt b/vector/src/main/java/im/vector/riotx/features/share/IncomingShareAction.kt index af98d3195e..0379c4980f 100644 --- a/vector/src/main/java/im/vector/riotx/features/share/IncomingShareAction.kt +++ b/vector/src/main/java/im/vector/riotx/features/share/IncomingShareAction.kt @@ -19,9 +19,10 @@ package im.vector.riotx.features.share import im.vector.matrix.android.api.session.room.model.RoomSummary 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() - object ShareToSelectedRooms: IncomingShareAction() + object ShareToSelectedRooms : IncomingShareAction() + data class ShareMedia(val keepOriginalSize: Boolean) : IncomingShareAction() data class FilterWith(val filter: String) : IncomingShareAction() - data class UpdateSharedData(val sharedData: SharedData): IncomingShareAction() + data class UpdateSharedData(val sharedData: SharedData) : IncomingShareAction() } diff --git a/vector/src/main/java/im/vector/riotx/features/share/IncomingShareFragment.kt b/vector/src/main/java/im/vector/riotx/features/share/IncomingShareFragment.kt index 2a2dfee6be..81ae6e2fd9 100644 --- a/vector/src/main/java/im/vector/riotx/features/share/IncomingShareFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/share/IncomingShareFragment.kt @@ -16,6 +16,7 @@ package im.vector.riotx.features.share +import android.app.Activity import android.content.ClipDescription import android.content.Intent 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.checkPermissions 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 kotlinx.android.synthetic.main.fragment_incoming_share.* import javax.inject.Inject @@ -98,11 +101,31 @@ class IncomingShareFragment @Inject constructor( } incomingShareViewModel.observeViewEvents { when (it) { - is IncomingShareViewEvents.ShareToRoom -> handleShareToRoom(it) + is IncomingShareViewEvents.ShareToRoom -> handleShareToRoom(it) + is IncomingShareViewEvents.EditMediaBeforeSending -> handleEditMediaBeforeSending(it) }.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() { super.onResume() @@ -189,7 +212,7 @@ class IncomingShareFragment @Inject constructor( } override fun invalidate() = withState(incomingShareViewModel) { - sendShareButton.isVisible = it.multiSelectionEnabled + sendShareButton.isVisible = it.isInMultiSelectionMode incomingShareController.setData(it) } diff --git a/vector/src/main/java/im/vector/riotx/features/share/IncomingShareViewEvents.kt b/vector/src/main/java/im/vector/riotx/features/share/IncomingShareViewEvents.kt index ebd6997760..6195e21753 100644 --- a/vector/src/main/java/im/vector/riotx/features/share/IncomingShareViewEvents.kt +++ b/vector/src/main/java/im/vector/riotx/features/share/IncomingShareViewEvents.kt @@ -16,9 +16,11 @@ 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.riotx.core.platform.VectorViewEvents sealed class IncomingShareViewEvents : VectorViewEvents { data class ShareToRoom(val roomSummary: RoomSummary, val sharedData: SharedData, val showAlert: Boolean) : IncomingShareViewEvents() + data class EditMediaBeforeSending(val contentAttachmentData: List) : IncomingShareViewEvents() } diff --git a/vector/src/main/java/im/vector/riotx/features/share/IncomingShareViewModel.kt b/vector/src/main/java/im/vector/riotx/features/share/IncomingShareViewModel.kt index 381756ddaf..2684828dec 100644 --- a/vector/src/main/java/im/vector/riotx/features/share/IncomingShareViewModel.kt +++ b/vector/src/main/java/im/vector/riotx/features/share/IncomingShareViewModel.kt @@ -24,12 +24,14 @@ import com.squareup.inject.assisted.Assisted import com.squareup.inject.assisted.AssistedInject import im.vector.matrix.android.api.query.QueryStringValue 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.roomSummaryQueryParams import im.vector.matrix.rx.rx import im.vector.riotx.core.extensions.exhaustive 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 java.util.concurrent.TimeUnit @@ -92,6 +94,7 @@ class IncomingShareViewModel @AssistedInject constructor(@Assisted initialState: when (action) { is IncomingShareAction.SelectRoom -> handleSelectRoom(action) is IncomingShareAction.ShareToSelectedRooms -> handleShareToSelectedRooms() + is IncomingShareAction.ShareMedia -> handleShareMediaToSelectedRooms(action) is IncomingShareAction.FilterWith -> handleFilter(action) is IncomingShareAction.UpdateSharedData -> handleUpdateSharedData(action) }.exhaustive @@ -108,34 +111,70 @@ class IncomingShareViewModel @AssistedInject constructor(@Assisted initialState: private fun handleShareToSelectedRooms() = withState { state -> val sharedData = state.sharedData ?: return@withState 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 selectedRoom = state.roomSummaries()?.find { it.roomId == selectedRoomId } ?: return@withState _viewEvents.post(IncomingShareViewEvents.ShareToRoom(selectedRoom, sharedData, showAlert = false)) } else { - state.selectedRoomIds.forEach { roomId -> - val room = session.getRoom(roomId) - if (sharedData is SharedData.Text) { - room?.sendTextMessage(sharedData.text) - } else if (sharedData is SharedData.Attachments) { - room?.sendMedias(sharedData.attachmentData) + when (sharedData) { + is SharedData.Text -> { + state.selectedRoomIds.forEach { roomId -> + val room = session.getRoom(roomId) + room?.sendTextMessage(sharedData.text) + } + } + is SharedData.Attachments -> { + shareAttachments(sharedData.attachmentData, state.selectedRoomIds, proposeMediaEdition = true, compressMediaBeforeSending = false) } } } } - private fun handleSelectRoom(action: IncomingShareAction.SelectRoom) = withState { - if (it.multiSelectionEnabled) { - val selectedRooms = it.selectedRoomIds + private fun shareAttachments(attachmentData: List, + selectedRoomIds: Set, + 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)) { selectedRooms.minus(action.roomSummary.roomId) } else { selectedRooms.plus(action.roomSummary.roomId) } - setState { copy(multiSelectionEnabled = newSelectedRooms.isNotEmpty(), selectedRoomIds = newSelectedRooms) } + setState { copy(isInMultiSelectionMode = newSelectedRooms.isNotEmpty(), selectedRoomIds = newSelectedRooms) } } else if (action.enableMultiSelect) { - setState { copy(multiSelectionEnabled = true, selectedRoomIds = setOf(action.roomSummary.roomId)) } + setState { copy(isInMultiSelectionMode = true, selectedRoomIds = setOf(action.roomSummary.roomId)) } } else { - val sharedData = it.sharedData ?: return@withState + val sharedData = state.sharedData ?: return@withState _viewEvents.post(IncomingShareViewEvents.ShareToRoom(action.roomSummary, sharedData, showAlert = true)) } } diff --git a/vector/src/main/java/im/vector/riotx/features/share/IncomingShareViewState.kt b/vector/src/main/java/im/vector/riotx/features/share/IncomingShareViewState.kt index cad1dd7d9c..0116d59348 100644 --- a/vector/src/main/java/im/vector/riotx/features/share/IncomingShareViewState.kt +++ b/vector/src/main/java/im/vector/riotx/features/share/IncomingShareViewState.kt @@ -26,7 +26,7 @@ data class IncomingShareViewState( val roomSummaries: Async> = Uninitialized, val filteredRoomSummaries: Async> = Uninitialized, val selectedRoomIds: Set = emptySet(), - val multiSelectionEnabled: Boolean = false + val isInMultiSelectionMode: Boolean = false ) : MvRxState diff --git a/vector/src/main/res/layout/fragment_attachments_preview.xml b/vector/src/main/res/layout/fragment_attachments_preview.xml index ba1039eda7..18009cd1a6 100644 --- a/vector/src/main/res/layout/fragment_attachments_preview.xml +++ b/vector/src/main/res/layout/fragment_attachments_preview.xml @@ -39,11 +39,24 @@ android:layout_width="0dp" android:layout_height="wrap_content" 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" /> + + + app:layout_constraintTop_toBottomOf="@+id/attachmentPreviewerMiniatureList" + tools:text="@plurals/send_images_with_original_size" /> diff --git a/vector/src/main/res/values/strings_riotX.xml b/vector/src/main/res/values/strings_riotX.xml index 8799d56ce6..017e206c00 100644 --- a/vector/src/main/res/values/strings_riotX.xml +++ b/vector/src/main/res/values/strings_riotX.xml @@ -22,6 +22,10 @@ Removeā€¦ Do you want to send this attachment to %1$s? + + Send image with the original size + Send images with the original size +