Skip to content

Commit 80bc3af

Browse files
authored
Merge pull request #5349 from vector-im/feature/mna/5005-save-image
#5005: Add save media icon in gallery
2 parents d5cb315 + 562780a commit 80bc3af

File tree

15 files changed

+512
-84
lines changed

15 files changed

+512
-84
lines changed

changelog.d/5005.feature

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Add possibility to save media from Gallery + reorder choices in message context menu

library/attachment-viewer/src/main/java/im/vector/lib/attachmentviewer/AttachmentViewerActivity.kt

+7-4
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@ import kotlin.math.abs
4545

4646
abstract class AttachmentViewerActivity : AppCompatActivity(), AttachmentEventListener {
4747

48+
protected val rootView: View
49+
get() = views.rootContainer
4850
protected val pager2: ViewPager2
4951
get() = views.attachmentPager
5052
protected val imageTransitionView: ImageView
@@ -298,10 +300,11 @@ abstract class AttachmentViewerActivity : AppCompatActivity(), AttachmentEventLi
298300

299301
private fun createSwipeToDismissHandler(): SwipeToDismissHandler =
300302
SwipeToDismissHandler(
301-
swipeView = views.dismissContainer,
302-
shouldAnimateDismiss = { shouldAnimateDismiss() },
303-
onDismiss = { animateClose() },
304-
onSwipeViewMove = ::handleSwipeViewMove)
303+
swipeView = views.dismissContainer,
304+
shouldAnimateDismiss = { shouldAnimateDismiss() },
305+
onDismiss = { animateClose() },
306+
onSwipeViewMove = ::handleSwipeViewMove
307+
)
305308

306309
private fun createSwipeDirectionDetector() =
307310
SwipeDirectionDetector(this) { swipeDirection = it }

vector/src/main/java/im/vector/app/core/di/MavericksViewModelModule.kt

+6
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ import im.vector.app.features.login.LoginViewModel
5858
import im.vector.app.features.login2.LoginViewModel2
5959
import im.vector.app.features.login2.created.AccountCreatedViewModel
6060
import im.vector.app.features.matrixto.MatrixToBottomSheetViewModel
61+
import im.vector.app.features.media.VectorAttachmentViewerViewModel
6162
import im.vector.app.features.onboarding.OnboardingViewModel
6263
import im.vector.app.features.poll.create.CreatePollViewModel
6364
import im.vector.app.features.qrcode.QrCodeScannerViewModel
@@ -594,4 +595,9 @@ interface MavericksViewModelModule {
594595
@IntoMap
595596
@MavericksViewModelKey(LocationSharingViewModel::class)
596597
fun createLocationSharingViewModelFactory(factory: LocationSharingViewModel.Factory): MavericksAssistedViewModelFactory<*, *>
598+
599+
@Binds
600+
@IntoMap
601+
@MavericksViewModelKey(VectorAttachmentViewerViewModel::class)
602+
fun vectorAttachmentViewerViewModelFactory(factory: VectorAttachmentViewerViewModel.Factory): MavericksAssistedViewModelFactory<*, *>
597603
}

vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionsViewModel.kt

+20-20
Original file line numberDiff line numberDiff line change
@@ -343,24 +343,6 @@ class MessageActionsViewModel @AssistedInject constructor(@Assisted
343343
add(EventSharedAction.Edit(eventId, timelineEvent.root.getClearType()))
344344
}
345345

346-
if (canRedact(timelineEvent, actionPermissions)) {
347-
if (timelineEvent.root.getClearType() == EventType.POLL_START) {
348-
add(EventSharedAction.Redact(
349-
eventId,
350-
askForReason = informationData.senderId != session.myUserId,
351-
dialogTitleRes = R.string.delete_poll_dialog_title,
352-
dialogDescriptionRes = R.string.delete_poll_dialog_content
353-
))
354-
} else {
355-
add(EventSharedAction.Redact(
356-
eventId,
357-
askForReason = informationData.senderId != session.myUserId,
358-
dialogTitleRes = R.string.delete_event_dialog_title,
359-
dialogDescriptionRes = R.string.delete_event_dialog_content
360-
))
361-
}
362-
}
363-
364346
if (canCopy(msgType)) {
365347
// TODO copy images? html? see ClipBoard
366348
add(EventSharedAction.Copy(messageContent!!.body))
@@ -382,12 +364,30 @@ class MessageActionsViewModel @AssistedInject constructor(@Assisted
382364
add(EventSharedAction.ViewEditHistory(informationData))
383365
}
384366

367+
if (canSave(msgType) && messageContent is MessageWithAttachmentContent) {
368+
add(EventSharedAction.Save(timelineEvent.eventId, messageContent))
369+
}
370+
385371
if (canShare(msgType)) {
386372
add(EventSharedAction.Share(timelineEvent.eventId, messageContent!!))
387373
}
388374

389-
if (canSave(msgType) && messageContent is MessageWithAttachmentContent) {
390-
add(EventSharedAction.Save(timelineEvent.eventId, messageContent))
375+
if (canRedact(timelineEvent, actionPermissions)) {
376+
if (timelineEvent.root.getClearType() == EventType.POLL_START) {
377+
add(EventSharedAction.Redact(
378+
eventId,
379+
askForReason = informationData.senderId != session.myUserId,
380+
dialogTitleRes = R.string.delete_poll_dialog_title,
381+
dialogDescriptionRes = R.string.delete_poll_dialog_content
382+
))
383+
} else {
384+
add(EventSharedAction.Redact(
385+
eventId,
386+
askForReason = informationData.senderId != session.myUserId,
387+
dialogTitleRes = R.string.delete_event_dialog_title,
388+
dialogDescriptionRes = R.string.delete_event_dialog_content
389+
))
390+
}
391391
}
392392
}
393393

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
/*
2+
* Copyright (c) 2022 New Vector Ltd
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package im.vector.app.features.media
18+
19+
interface AttachmentInteractionListener {
20+
fun onDismiss()
21+
fun onShare()
22+
fun onDownload()
23+
fun onPlayPause(play: Boolean)
24+
fun videoSeekTo(percent: Int)
25+
}

vector/src/main/java/im/vector/app/features/media/AttachmentOverlayView.kt

+10-12
Original file line numberDiff line numberDiff line change
@@ -30,35 +30,33 @@ class AttachmentOverlayView @JvmOverloads constructor(
3030
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
3131
) : ConstraintLayout(context, attrs, defStyleAttr), AttachmentEventListener {
3232

33-
var onShareCallback: (() -> Unit)? = null
34-
var onBack: (() -> Unit)? = null
35-
var onPlayPause: ((play: Boolean) -> Unit)? = null
36-
var videoSeekTo: ((progress: Int) -> Unit)? = null
37-
33+
var interactionListener: AttachmentInteractionListener? = null
3834
val views: MergeImageAttachmentOverlayBinding
3935

40-
var isPlaying = false
41-
42-
var suspendSeekBarUpdate = false
36+
private var isPlaying = false
37+
private var suspendSeekBarUpdate = false
4338

4439
init {
4540
inflate(context, R.layout.merge_image_attachment_overlay, this)
4641
views = MergeImageAttachmentOverlayBinding.bind(this)
4742
setBackgroundColor(Color.TRANSPARENT)
4843
views.overlayBackButton.setOnClickListener {
49-
onBack?.invoke()
44+
interactionListener?.onDismiss()
5045
}
5146
views.overlayShareButton.setOnClickListener {
52-
onShareCallback?.invoke()
47+
interactionListener?.onShare()
48+
}
49+
views.overlayDownloadButton.setOnClickListener {
50+
interactionListener?.onDownload()
5351
}
5452
views.overlayPlayPauseButton.setOnClickListener {
55-
onPlayPause?.invoke(!isPlaying)
53+
interactionListener?.onPlayPause(!isPlaying)
5654
}
5755

5856
views.overlaySeekBar.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener {
5957
override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) {
6058
if (fromUser) {
61-
videoSeekTo?.invoke(progress)
59+
interactionListener?.videoSeekTo(progress)
6260
}
6361
}
6462

vector/src/main/java/im/vector/app/features/media/BaseAttachmentProvider.kt

+2-20
Original file line numberDiff line numberDiff line change
@@ -49,14 +49,7 @@ abstract class BaseAttachmentProvider<Type>(
4949
private val stringProvider: StringProvider
5050
) : AttachmentSourceProvider {
5151

52-
interface InteractionListener {
53-
fun onDismissTapped()
54-
fun onShareTapped()
55-
fun onPlayPause(play: Boolean)
56-
fun videoSeekTo(percent: Int)
57-
}
58-
59-
var interactionListener: InteractionListener? = null
52+
var interactionListener: AttachmentInteractionListener? = null
6053

6154
private var overlayView: AttachmentOverlayView? = null
6255

@@ -68,18 +61,7 @@ abstract class BaseAttachmentProvider<Type>(
6861
if (position == -1) return null
6962
if (overlayView == null) {
7063
overlayView = AttachmentOverlayView(context)
71-
overlayView?.onBack = {
72-
interactionListener?.onDismissTapped()
73-
}
74-
overlayView?.onShareCallback = {
75-
interactionListener?.onShareTapped()
76-
}
77-
overlayView?.onPlayPause = { play ->
78-
interactionListener?.onPlayPause(play)
79-
}
80-
overlayView?.videoSeekTo = { percent ->
81-
interactionListener?.videoSeekTo(percent)
82-
}
64+
overlayView?.interactionListener = interactionListener
8365
}
8466

8567
val timelineEvent = getTimelineEventAtPosition(position)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/*
2+
* Copyright (c) 2022 New Vector Ltd
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package im.vector.app.features.media
18+
19+
import im.vector.app.core.platform.VectorViewModelAction
20+
import java.io.File
21+
22+
sealed class VectorAttachmentViewerAction : VectorViewModelAction {
23+
data class DownloadMedia(val file: File) : VectorAttachmentViewerAction()
24+
}

0 commit comments

Comments
 (0)