Skip to content

Commit 8bd212f

Browse files
authored
Merge pull request #5756 from kikoso/feat/5609-swipe-to-close-media
feat: allow closing photos and videos with a swipe gesture (#5609)
2 parents d2c9cf9 + 5037a8c commit 8bd212f

File tree

5 files changed

+116
-5
lines changed

5 files changed

+116
-5
lines changed

app/src/main/java/com/nextcloud/talk/fullscreenfile/FullScreenImageActivity.kt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,15 @@
55
* SPDX-FileCopyrightText: 2021 Andy Scherzinger <[email protected]>
66
* SPDX-FileCopyrightText: 2021 Marcel Hibbe <[email protected]>
77
* SPDX-FileCopyrightText: 2021 Dariusz Olszewski <[email protected]>
8+
* SPDX-FileCopyrightText: 2026 Enrique López-Mañas <[email protected]>
89
* SPDX-License-Identifier: GPL-3.0-or-later
910
*/
1011
package com.nextcloud.talk.fullscreenfile
1112

1213
import android.content.Intent
1314
import android.os.Bundle
1415
import android.util.Log
16+
import com.nextcloud.talk.ui.SwipeToCloseLayout
1517
import android.view.Menu
1618
import android.view.MenuItem
1719
import android.view.View
@@ -133,6 +135,12 @@ class FullScreenImageActivity : AppCompatActivity() {
133135
binding.photoView.visibility = View.VISIBLE
134136
displayImage(path)
135137
}
138+
139+
binding.swipeToCloseLayout.setOnSwipeToCloseListener(object : SwipeToCloseLayout.OnSwipeToCloseListener {
140+
override fun onSwipeToClose() {
141+
finish()
142+
}
143+
})
136144
}
137145

138146
private fun displayImage(path: String) {

app/src/main/java/com/nextcloud/talk/fullscreenfile/FullScreenMediaActivity.kt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
* SPDX-FileCopyrightText: 2023 Parneet Singh <[email protected]>
66
* SPDX-FileCopyrightText: 2021 Andy Scherzinger <[email protected]>
77
* SPDX-FileCopyrightText: 2021 Marcel Hibbe <[email protected]>
8+
* SPDX-FileCopyrightText: 2026 Enrique López-Mañas <[email protected]>
89
* SPDX-License-Identifier: GPL-3.0-or-later
910
*/
1011
package com.nextcloud.talk.fullscreenfile
@@ -39,6 +40,7 @@ import com.nextcloud.talk.BuildConfig
3940
import com.nextcloud.talk.R
4041
import com.nextcloud.talk.application.NextcloudTalkApplication
4142
import com.nextcloud.talk.databinding.ActivityFullScreenMediaBinding
43+
import com.nextcloud.talk.ui.SwipeToCloseLayout
4244
import com.nextcloud.talk.ui.dialog.SaveToStorageDialogFragment
4345
import com.nextcloud.talk.utils.Mimetype.VIDEO_PREFIX_GENERIC
4446
import java.io.File
@@ -135,6 +137,12 @@ class FullScreenMediaActivity : AppCompatActivity() {
135137
}
136138
}
137139
)
140+
141+
binding.swipeToCloseLayout.setOnSwipeToCloseListener(object : SwipeToCloseLayout.OnSwipeToCloseListener {
142+
override fun onSwipeToClose() {
143+
finish()
144+
}
145+
})
138146
}
139147

140148
override fun onStart() {
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
/*
2+
* Nextcloud Talk - Android Client
3+
*
4+
* SPDX-FileCopyrightText: 2026 Enrique López-Mañas <[email protected]>
5+
* SPDX-License-Identifier: GPL-3.0-or-later
6+
*/
7+
package com.nextcloud.talk.ui
8+
9+
import android.content.Context
10+
import android.util.AttributeSet
11+
import android.view.MotionEvent
12+
import android.view.View
13+
import android.widget.FrameLayout
14+
import androidx.customview.widget.ViewDragHelper
15+
import kotlin.math.abs
16+
17+
class SwipeToCloseLayout @JvmOverloads constructor(
18+
context: Context,
19+
attrs: AttributeSet? = null,
20+
defStyleAttr: Int = 0
21+
) : FrameLayout(context, attrs, defStyleAttr) {
22+
23+
private var dragHelper: ViewDragHelper
24+
private var swipeListener: OnSwipeToCloseListener? = null
25+
26+
interface OnSwipeToCloseListener {
27+
fun onSwipeToClose()
28+
}
29+
30+
init {
31+
dragHelper = ViewDragHelper.create(this, 1.0f, DragCallback())
32+
}
33+
34+
fun setOnSwipeToCloseListener(listener: OnSwipeToCloseListener) {
35+
this.swipeListener = listener
36+
}
37+
38+
override fun onInterceptTouchEvent(ev: MotionEvent): Boolean {
39+
val action = ev.actionMasked
40+
if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {
41+
dragHelper.cancel()
42+
return false
43+
}
44+
return dragHelper.shouldInterceptTouchEvent(ev)
45+
}
46+
47+
override fun onTouchEvent(ev: MotionEvent): Boolean {
48+
dragHelper.processTouchEvent(ev)
49+
return true
50+
}
51+
52+
private inner class DragCallback : ViewDragHelper.Callback() {
53+
override fun tryCaptureView(child: View, pointerId: Int): Boolean {
54+
return true // Capture any child view
55+
}
56+
57+
override fun getViewVerticalDragRange(child: View): Int {
58+
return height
59+
}
60+
61+
override fun clampViewPositionVertical(child: View, therapeutic: Int, dy: Int): Int {
62+
return therapeutic
63+
}
64+
65+
override fun onViewReleased(releasedChild: View, xvel: Float, yvel: Float) {
66+
val totalDragDistance = abs(releasedChild.top)
67+
if (totalDragDistance > height * DRAG_THRESHOLD || abs(yvel) > dragHelper.minVelocity) {
68+
swipeListener?.onSwipeToClose()
69+
} else {
70+
dragHelper.settleCapturedViewAt(0, 0)
71+
invalidate()
72+
}
73+
}
74+
75+
override fun onViewPositionChanged(changedView: View, left: Int, top: Int, dx: Int, dy: Int) {
76+
val progress = 1f - (abs(top).toFloat() / height)
77+
alpha = progress.coerceIn(MIN_ALPHA, MAX_ALPHA)
78+
}
79+
}
80+
81+
override fun computeScroll() {
82+
if (dragHelper.continueSettling(true)) {
83+
postInvalidateOnAnimation()
84+
}
85+
}
86+
87+
companion object {
88+
private const val DRAG_THRESHOLD = 0.3f
89+
private const val MIN_ALPHA = 0.5f
90+
private const val MAX_ALPHA = 1.0f
91+
}
92+
}

app/src/main/res/layout/activity_full_screen_image.xml

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,13 @@
55
~ SPDX-FileCopyrightText: 2021 Andy Scherzinger <[email protected]>
66
~ SPDX-FileCopyrightText: 2021 Marcel Hibbe <[email protected]>
77
~ SPDX-FileCopyrightText: 2021 Dariusz Olszewski <[email protected]>
8+
~ SPDX-FileCopyrightText: 2026 Enrique López-Mañas <[email protected]>
89
~ SPDX-License-Identifier: GPL-3.0-or-later
910
-->
10-
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
11+
<com.nextcloud.talk.ui.SwipeToCloseLayout xmlns:android="http://schemas.android.com/apk/res/android"
1112
xmlns:tools="http://schemas.android.com/tools"
1213
xmlns:app="http://schemas.android.com/apk/res-auto"
13-
android:id="@+id/image_wrapper_view"
14+
android:id="@+id/swipe_to_close_layout"
1415
android:layout_width="match_parent"
1516
android:layout_height="match_parent"
1617
tools:context=".fullscreenfile.FullScreenImageActivity">
@@ -35,4 +36,4 @@
3536
android:layout_height="match_parent"
3637
android:visibility="invisible" />
3738

38-
</FrameLayout>
39+
</com.nextcloud.talk.ui.SwipeToCloseLayout>

app/src/main/res/layout/activity_full_screen_media.xml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,13 @@
44
~
55
~ SPDX-FileCopyrightText: 2021 Andy Scherzinger <[email protected]>
66
~ SPDX-FileCopyrightText: 2021 Marcel Hibbe <[email protected]>
7+
~ SPDX-FileCopyrightText: 2026 Enrique López-Mañas <[email protected]>
78
~ SPDX-License-Identifier: GPL-3.0-or-later
89
-->
9-
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
10+
<com.nextcloud.talk.ui.SwipeToCloseLayout xmlns:android="http://schemas.android.com/apk/res/android"
1011
xmlns:app="http://schemas.android.com/apk/res-auto"
1112
xmlns:tools="http://schemas.android.com/tools"
13+
android:id="@+id/swipe_to_close_layout"
1214
android:layout_width="match_parent"
1315
android:layout_height="match_parent"
1416
tools:context=".fullscreenfile.FullScreenMediaActivity">
@@ -28,4 +30,4 @@
2830
app:show_buffering="when_playing"
2931
app:show_shuffle_button="true" />
3032

31-
</FrameLayout>
33+
</com.nextcloud.talk.ui.SwipeToCloseLayout>

0 commit comments

Comments
 (0)