Skip to content

Commit cdfabae

Browse files
Merge pull request #17408 from nextcloud/fix/toggle-full-screen-icon
fix(media-preview): toggle full screen icon
2 parents 890238d + 4b4ea55 commit cdfabae

8 files changed

Lines changed: 93 additions & 43 deletions

File tree

app/src/main/java/com/nextcloud/utils/extensions/PlayerViewExtensions.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,10 @@ fun PlayerView.applyControlsInsets(insets: Insets) {
2121

2222
@OptIn(UnstableApi::class)
2323
fun PlayerView.setFullscreenButton(isFullscreen: Boolean, onClick: () -> Unit) {
24+
// PlayerControlView only applies fullscreen_enter_icon/fullscreen_exit_icon when the state
25+
// actually changes, so flip it first to force the configured drawable onto the button.
2426
setFullscreenButtonClickListener(null)
27+
setFullscreenButtonState(!isFullscreen)
2528
setFullscreenButtonState(isFullscreen)
2629
setFullscreenButtonClickListener { onClick() }
2730
}

app/src/main/java/com/owncloud/android/ui/preview/PreviewMediaActivity.kt

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,10 @@ class PreviewMediaActivity :
129129
private val sendShareDownloader by lazy { SendShareDownloader(this) }
130130

131131
private lateinit var binding: ActivityPreviewMediaBinding
132+
133+
private val exoplayerView: PlayerView
134+
get() = binding.exoplayerView.root
135+
132136
private var emptyListView: ViewGroup? = null
133137
private var videoPlayer: ExoPlayer? = null
134138
private var videoMediaSession: MediaSession? = null
@@ -211,7 +215,7 @@ class PreviewMediaActivity :
211215
return
212216
}
213217

214-
binding.exoplayerView.visibility = if (isFileVideo()) View.VISIBLE else View.GONE
218+
exoplayerView.visibility = if (isFileVideo()) View.VISIBLE else View.GONE
215219
binding.imagePreview.visibility = if (isFileVideo()) View.GONE else View.VISIBLE
216220

217221
if (isFileVideo()) {
@@ -339,7 +343,7 @@ class PreviewMediaActivity :
339343
addListener(
340344
ExoplayerListener(
341345
this@PreviewMediaActivity,
342-
binding.exoplayerView,
346+
exoplayerView,
343347
this
344348
)
345349
)
@@ -451,7 +455,7 @@ class PreviewMediaActivity :
451455
}
452456

453457
private fun applyWindowInsets() {
454-
val playerView = binding.exoplayerView
458+
val playerView = exoplayerView
455459
val exoControls = playerView.findViewById<FrameLayout>(androidx.media3.ui.R.id.exo_bottom_bar)
456460
val exoProgress = playerView.findViewById<DefaultTimeBar>(androidx.media3.ui.R.id.exo_progress)
457461
val progressBottomMargin = exoProgress.marginBottom
@@ -481,7 +485,7 @@ class PreviewMediaActivity :
481485
private fun setupVideoView() {
482486
initWindowInsetsController()
483487
val type = WindowInsetsCompat.Type.systemBars()
484-
binding.exoplayerView.let {
488+
exoplayerView.let {
485489
it.setShowNextButton(false)
486490
it.setShowPreviousButton(false)
487491
it.setControllerVisibilityListener(
@@ -507,7 +511,7 @@ class PreviewMediaActivity :
507511
this,
508512
client,
509513
player,
510-
binding.exoplayerView
514+
exoplayerView
511515
)
512516
.apply {
513517
setOnDismissListener {

app/src/main/java/com/owncloud/android/ui/preview/PreviewMediaFragment.kt

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ import androidx.media3.common.Player
4343
import androidx.media3.common.util.UnstableApi
4444
import androidx.media3.exoplayer.ExoPlayer
4545
import androidx.media3.session.MediaSession
46+
import androidx.media3.ui.PlayerView
4647
import com.nextcloud.client.account.User
4748
import com.nextcloud.client.account.UserAccountManager
4849
import com.nextcloud.client.di.Injectable
@@ -117,6 +118,10 @@ class PreviewMediaFragment :
117118
lateinit var backgroundJobManager: BackgroundJobManager
118119

119120
lateinit var binding: FragmentPreviewMediaBinding
121+
122+
private val exoplayerView: PlayerView
123+
get() = binding.exoplayerView.root
124+
120125
private var emptyListView: ViewGroup? = null
121126
private var exoPlayer: ExoPlayer? = null
122127
private var mediaSession: MediaSession? = null
@@ -190,7 +195,7 @@ class PreviewMediaFragment :
190195
private fun applyWindowInsets() {
191196
binding.root.post {
192197
val rootInsets = ViewCompat.getRootWindowInsets(binding.root) ?: return@post
193-
binding.exoplayerView.applyControlsInsets(
198+
exoplayerView.applyControlsInsets(
194199
rootInsets.getInsets(
195200
WindowInsetsCompat.Type.systemBars() or WindowInsetsCompat.Type.displayCutout()
196201
)
@@ -267,7 +272,7 @@ class PreviewMediaFragment :
267272
private fun createExoPlayer(context: Context, client: NextcloudClient) {
268273
exoPlayer = createNextcloudExoplayer(context, client)
269274
exoPlayer?.let {
270-
val listener = ExoplayerListener(context, binding.exoplayerView, it) { goBackToLivePhoto() }
275+
val listener = ExoplayerListener(context, exoplayerView, it) { goBackToLivePhoto() }
271276
it.addListener(listener)
272277
}
273278
mediaSession = MediaSession.Builder(
@@ -305,7 +310,7 @@ class PreviewMediaFragment :
305310

306311
@OptIn(UnstableApi::class)
307312
private fun setupVideoView() {
308-
binding.exoplayerView.run {
313+
exoplayerView.run {
309314
setShowNextButton(false)
310315
setShowPreviousButton(false)
311316
player = exoPlayer
@@ -489,7 +494,7 @@ class PreviewMediaFragment :
489494

490495
@SuppressLint("ClickableViewAccessibility")
491496
override fun onTouch(v: View, event: MotionEvent): Boolean {
492-
if (event.action == MotionEvent.ACTION_DOWN && v == binding.exoplayerView) {
497+
if (event.action == MotionEvent.ACTION_DOWN && v == exoplayerView) {
493498
// added a margin on the left to avoid interfering with gesture to open navigation drawer
494499
if (event.x / Resources.getSystem().displayMetrics.density > MIN_DENSITY_RATIO) {
495500
startFullScreenVideo()
@@ -509,7 +514,7 @@ class PreviewMediaFragment :
509514
activity,
510515
client,
511516
player,
512-
binding.exoplayerView
517+
exoplayerView
513518
).apply {
514519
setOnDismissListener {
515520
isFullscreenActive = false

app/src/main/java/com/owncloud/android/ui/preview/PreviewVideoFullscreenDialog.kt

Lines changed: 40 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
package com.owncloud.android.ui.preview
1010

1111
import android.app.Dialog
12+
import android.content.DialogInterface
1213
import android.os.Build
1314
import android.view.ViewGroup
1415
import android.view.Window
@@ -48,7 +49,13 @@ class PreviewVideoFullscreenDialog(
4849
) : Dialog(sourceView.context, R.style.Dialog_FullscreenVideo) {
4950

5051
private val binding: DialogPreviewVideoBinding = DialogPreviewVideoBinding.inflate(layoutInflater)
52+
53+
private val playerView: PlayerView
54+
get() = binding.videoPlayer.root
55+
5156
private var playingStateListener: androidx.media3.common.Player.Listener? = null
57+
private var externalDismissListener: DialogInterface.OnDismissListener? = null
58+
private var wasPlayingBeforeDismiss = false
5259

5360
/**
5461
* exoPlayer instance used for this view, either the original one or a new one in specific cases.
@@ -72,12 +79,24 @@ class PreviewVideoFullscreenDialog(
7279
mExoPlayer = getExoPlayer(nextcloudClient)
7380
if (shouldUseRotatedVideoWorkaround) {
7481
sourceExoPlayer.currentMediaItem?.let { mExoPlayer.setMediaItem(it, sourceExoPlayer.currentPosition) }
75-
binding.videoPlayer.player = mExoPlayer
82+
playerView.player = mExoPlayer
7683
mExoPlayer.prepare()
7784
}
85+
super.setOnDismissListener {
86+
restoreSourcePlayer()
87+
externalDismissListener?.onDismiss(this)
88+
}
7889
handleOnBackPressed()
7990
}
8091

92+
/**
93+
* Keeps the caller's listener instead of letting it replace the internal one, which has to run first to hand the
94+
* playback back to [sourceView].
95+
*/
96+
override fun setOnDismissListener(listener: DialogInterface.OnDismissListener?) {
97+
externalDismissListener = listener
98+
}
99+
81100
private fun isRotatedVideo(): Boolean {
82101
val videoFormat = sourceExoPlayer.videoFormat
83102
return videoFormat != null && videoFormat.rotationDegrees != 0
@@ -88,7 +107,7 @@ class PreviewVideoFullscreenDialog(
88107
NextcloudExoPlayer
89108
.createNextcloudExoplayer(sourceView.context, nextcloudClient)
90109
.apply {
91-
addListener(ExoplayerListener(sourceView.context, binding.videoPlayer, this))
110+
addListener(ExoplayerListener(sourceView.context, playerView, this))
92111
}
93112
} else {
94113
sourceExoPlayer
@@ -103,7 +122,7 @@ class PreviewVideoFullscreenDialog(
103122
enableImmersiveMode()
104123
keepControlsClearOfSystemBars()
105124
switchTargetViewFromSource()
106-
binding.videoPlayer.setFullscreenButton(isFullscreen = true) {
125+
playerView.setFullscreenButton(isFullscreen = true) {
107126
activity.onBackPressedDispatcher.onBackPressed()
108127
}
109128
if (isPlaying) {
@@ -117,36 +136,37 @@ class PreviewVideoFullscreenDialog(
117136
if (shouldUseRotatedVideoWorkaround) {
118137
mExoPlayer.seekTo(sourceExoPlayer.currentPosition)
119138
} else {
120-
PlayerView.switchTargetView(sourceExoPlayer, sourceView, binding.videoPlayer)
139+
PlayerView.switchTargetView(sourceExoPlayer, sourceView, playerView)
121140
}
122141
}
123142

124143
private fun handleOnBackPressed() {
125144
activity.onBackPressedDispatcher.addCallback(activity) {
126-
val isPlaying = mExoPlayer.isPlaying
127-
if (isPlaying) {
145+
wasPlayingBeforeDismiss = mExoPlayer.isPlaying
146+
if (wasPlayingBeforeDismiss) {
128147
mExoPlayer.pause()
129148
}
130-
setOnDismissListener {
131-
playingStateListener?.let {
132-
mExoPlayer.removeListener(it)
133-
}
134-
switchTargetViewToSource()
135-
if (isPlaying) {
136-
sourceExoPlayer.play()
137-
}
138-
sourceView.showController()
139-
}
140149
dismiss()
141150
isEnabled = false
142151
}
143152
}
144153

154+
private fun restoreSourcePlayer() {
155+
playingStateListener?.let {
156+
mExoPlayer.removeListener(it)
157+
}
158+
switchTargetViewToSource()
159+
if (wasPlayingBeforeDismiss) {
160+
sourceExoPlayer.play()
161+
}
162+
sourceView.showController()
163+
}
164+
145165
private fun switchTargetViewToSource() {
146166
if (shouldUseRotatedVideoWorkaround) {
147167
sourceExoPlayer.seekTo(mExoPlayer.currentPosition)
148168
} else {
149-
PlayerView.switchTargetView(sourceExoPlayer, binding.videoPlayer, sourceView)
169+
PlayerView.switchTargetView(sourceExoPlayer, playerView, sourceView)
150170
}
151171
}
152172

@@ -165,15 +185,15 @@ class PreviewVideoFullscreenDialog(
165185
}
166186

167187
private fun keepControlsClearOfSystemBars() {
168-
ViewCompat.setOnApplyWindowInsetsListener(binding.videoPlayer) { _, windowInsets ->
169-
binding.videoPlayer.applyControlsInsets(
188+
ViewCompat.setOnApplyWindowInsetsListener(playerView) { _, windowInsets ->
189+
playerView.applyControlsInsets(
170190
windowInsets.getInsets(
171191
WindowInsetsCompat.Type.systemBars() or WindowInsetsCompat.Type.displayCutout()
172192
)
173193
)
174194
windowInsets
175195
}
176-
ViewCompat.requestApplyInsets(binding.videoPlayer)
196+
ViewCompat.requestApplyInsets(playerView)
177197
}
178198

179199
companion object {

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,15 +37,15 @@
3737
app:layout_constraintTop_toTopOf="parent" />
3838

3939

40-
<androidx.media3.ui.PlayerView
40+
<include
4141
android:id="@+id/exoplayer_view"
42+
layout="@layout/view_media_player"
4243
android:layout_width="match_parent"
4344
android:layout_height="match_parent"
4445
app:layout_constraintBottom_toBottomOf="parent"
4546
app:layout_constraintEnd_toEndOf="parent"
4647
app:layout_constraintStart_toStartOf="parent"
47-
app:layout_constraintTop_toTopOf="parent"
48-
app:show_buffering="always" />
48+
app:layout_constraintTop_toTopOf="parent" />
4949

5050
<com.owncloud.android.media.MediaControlView
5151
android:id="@+id/audio_controller_view"

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

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,21 @@
22
<!--
33
~ Nextcloud - Android Client
44
~
5+
~ SPDX-FileCopyrightText: 2026 Alper Ozturk <alper.ozturk@nextcloud.com>
56
~ SPDX-FileCopyrightText: 2021 Tobias Kaminsky <tobias@kaminsky.me>
67
~ SPDX-FileCopyrightText: 2021 Nextcloud GmbH
78
~ SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only
89
-->
9-
<androidx.media3.ui.PlayerView xmlns:android="http://schemas.android.com/apk/res/android"
10-
xmlns:app="http://schemas.android.com/apk/res-auto"
11-
android:id="@+id/videoPlayer"
10+
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
1211
android:layout_width="match_parent"
1312
android:layout_height="match_parent"
14-
android:layout_gravity="center"
15-
android:background="@color/black"
16-
app:show_buffering="always" />
13+
android:background="@color/black">
14+
15+
<include
16+
android:id="@+id/videoPlayer"
17+
layout="@layout/view_media_player"
18+
android:layout_width="match_parent"
19+
android:layout_height="match_parent"
20+
android:layout_gravity="center" />
21+
22+
</FrameLayout>

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
<!--
33
~ Nextcloud - Android Client
44
~
5+
~ SPDX-FileCopyrightText: 2026 Alper Ozturk <alper.ozturk@nextcloud.com>
56
~ SPDX-FileCopyrightText: 2023 Parneet Singh <gurayaparneet@gmail.com>
67
~ SPDX-FileCopyrightText: 2021 tobiasKaminsky <tobias@kaminsky.me>
78
~ SPDX-FileCopyrightText: 2020 Andy Scherzinger <info@andy-scherzinger.de>
@@ -18,12 +19,11 @@
1819
android:gravity="center"
1920
tools:context=".ui.preview.PreviewMediaFragment">
2021

21-
<androidx.media3.ui.PlayerView
22+
<include
2223
android:id="@+id/exoplayer_view"
24+
layout="@layout/view_media_player"
2325
android:layout_width="match_parent"
24-
android:layout_height="match_parent"
25-
android:layout_gravity="center"
26-
app:show_buffering="always" />
26+
android:layout_height="match_parent" />
2727

2828
<FrameLayout
2929
android:id="@+id/progress"
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<!--
3+
~ Nextcloud - Android Client
4+
~
5+
~ SPDX-FileCopyrightText: 2026 Alper Ozturk <alper.ozturk@nextcloud.com>
6+
~ SPDX-License-Identifier: AGPL-3.0-or-later
7+
-->
8+
<androidx.media3.ui.PlayerView xmlns:android="http://schemas.android.com/apk/res/android"
9+
xmlns:app="http://schemas.android.com/apk/res-auto"
10+
android:layout_width="match_parent"
11+
android:layout_height="match_parent"
12+
app:show_buffering="always" />

0 commit comments

Comments
 (0)