Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/*
* Nextcloud - Android Client
*
* SPDX-FileCopyrightText: 2026 Alper Ozturk <alper.ozturk@nextcloud.com>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
package com.nextcloud.utils.extensions

import android.view.View
import androidx.annotation.OptIn
import androidx.core.graphics.Insets
import androidx.core.view.updatePadding
import androidx.media3.common.util.UnstableApi
import androidx.media3.ui.PlayerView

@OptIn(UnstableApi::class)
fun PlayerView.applyControlsInsets(insets: Insets) {
val controller = findViewById<View>(androidx.media3.ui.R.id.exo_controller) ?: return
controller.updatePadding(left = insets.left, right = insets.right, bottom = insets.bottom)
}

@OptIn(UnstableApi::class)
fun PlayerView.setFullscreenButton(isFullscreen: Boolean, onClick: () -> Unit) {
setFullscreenButtonClickListener(null)
setFullscreenButtonState(isFullscreen)
setFullscreenButtonClickListener { onClick() }
}
Original file line number Diff line number Diff line change
Expand Up @@ -2403,7 +2403,7 @@ class FileDisplayActivity :

is PreviewMediaFragment -> {
fragment.updateFile(ocFile)
if (PreviewMediaFragment.canBePreviewed(ocFile)) {
if (PreviewMediaFragment.isAudioOrVideo(ocFile)) {
startMediaPreview(
ocFile,
fragment.position,
Expand Down Expand Up @@ -2688,7 +2688,7 @@ class FileDisplayActivity :
startMediaActivity(file, startPlaybackPosition, autoplay, actualUser)
} else {
configureToolbarForPreview(file)
val mediaFragment: Fragment = newInstance(file, user.get(), startPlaybackPosition, autoplay, false)
val mediaFragment: Fragment = newInstance(file, user.get(), startPlaybackPosition, autoplay)
setLeftFragment(mediaFragment, false)
}
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import com.nextcloud.client.account.User
import com.nextcloud.client.di.Injectable
import com.nextcloud.client.jobs.download.FileDownloadHelper.Companion.instance
Expand Down Expand Up @@ -136,7 +135,7 @@ class FileDownloadFragment :
private const val ARG_USER = "USER"
private val TAG: String = FileDownloadFragment::class.java.simpleName

fun newInstance(file: OCFile?, user: User?, ignoreFirstSavedState: Boolean): Fragment =
fun newInstance(file: OCFile?, user: User?, ignoreFirstSavedState: Boolean): FileDownloadFragment =
FileDownloadFragment().apply {
arguments = Bundle().apply {
putParcelable(ARG_FILE, file)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ import android.view.View
import androidx.activity.OnBackPressedCallback
import androidx.core.content.ContextCompat
import androidx.core.graphics.drawable.toDrawable
import androidx.core.view.ViewCompat
import androidx.core.view.WindowCompat
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.WindowInsetsControllerCompat
import androidx.drawerlayout.widget.DrawerLayout
import androidx.localbroadcastmanager.content.LocalBroadcastManager
import androidx.viewpager2.widget.ViewPager2
Expand Down Expand Up @@ -59,7 +63,7 @@ import kotlin.math.max
import kotlin.math.min

/**
* Holds a swiping gallery where image files contained in an Nextcloud directory are shown.
* Holds a swiping gallery where image and video files contained in a Nextcloud directory are shown.
*/
@Suppress("TooManyFunctions")
class PreviewImageActivity :
Expand All @@ -70,13 +74,15 @@ class PreviewImageActivity :
Injectable {
private var livePhotoFile: OCFile? = null
private var viewPager: ViewPager2? = null
private var previewImagePagerAdapter: PreviewImagePagerAdapter? = null
private var previewMediaPagerAdapter: PreviewMediaPagerAdapter? = null
private var savedPosition: Int? = null

private val downloadStartReceiver = DownloadStartReceiver()
private val downloadFinishReceiver = DownloadFinishReceiver()

private var fullScreenAnchorView: View? = null
private val windowInsetsController: WindowInsetsControllerCompat by lazy {
WindowCompat.getInsetsController(window, window.decorView)
}

private var isDownloadWorkStarted = false
private var screenState = PreviewImageActivityState.Idle
Expand Down Expand Up @@ -115,8 +121,6 @@ class PreviewImageActivity :
it.setBackgroundDrawable(R.color.black.toDrawable())
}

fullScreenAnchorView = window.decorView
// to keep our UI controls visibility in line with system bars visibility
setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED)

val requestWaitingForBinder = savedInstanceState?.getBoolean(KEY_WAITING_FOR_BINDER) ?: false
Expand Down Expand Up @@ -163,7 +167,7 @@ class PreviewImageActivity :
if (virtualFolderType != null && virtualFolderType !== VirtualFolderType.NONE) {
val type = virtualFolderType as VirtualFolderType

previewImagePagerAdapter = PreviewImagePagerAdapter(
previewMediaPagerAdapter = PreviewMediaPagerAdapter(
this,
type,
user,
Expand All @@ -174,7 +178,7 @@ class PreviewImageActivity :
val parentFolder = file?.let { storageManager.getFileById(it.parentId) }
?: storageManager.getFileByEncryptedRemotePath(OCFile.ROOT_PATH)

previewImagePagerAdapter = PreviewImagePagerAdapter(
previewMediaPagerAdapter = PreviewMediaPagerAdapter(
this,
livePhotoFile,
parentFolder,
Expand All @@ -192,11 +196,11 @@ class PreviewImageActivity :
) {
savedPosition
} else {
file?.let { previewImagePagerAdapter?.getFilePosition(it) }
file?.let { previewMediaPagerAdapter?.getFilePosition(it) }
}
position = position?.toDouble()?.let { max(it, 0.0).toInt() }

viewPager?.adapter = previewImagePagerAdapter
viewPager?.adapter = previewMediaPagerAdapter
viewPager?.registerOnPageChangeCallback(object : OnPageChangeCallback() {
override fun onPageSelected(position: Int) {
selectPage(position)
Expand Down Expand Up @@ -231,7 +235,7 @@ class PreviewImageActivity :

fun updateViewPagerAfterDeletionAndAdvanceForward() {
val deletePosition = viewPager?.currentItem ?: return
previewImagePagerAdapter?.let { adapter ->
previewMediaPagerAdapter?.let { adapter ->
val nextPosition = min(deletePosition, adapter.itemCount - 1)
viewPager?.setCurrentItem(nextPosition, true)
adapter.delete(deletePosition)
Expand Down Expand Up @@ -311,7 +315,7 @@ class PreviewImageActivity :
super.onRemoteOperationFinish(operation, result)

if (operation is RemoveFileOperation) {
previewImagePagerAdapter?.let {
previewMediaPagerAdapter?.let {
if (it.itemCount <= 1) {
backToDisplayActivity()
return
Expand Down Expand Up @@ -346,7 +350,7 @@ class PreviewImageActivity :
private fun setDownloadedItem() {
savedPosition?.let { position ->

previewImagePagerAdapter?.run {
previewMediaPagerAdapter?.run {
file?.let {
updateFile(position, it)
notifyItemChanged(position)
Expand Down Expand Up @@ -438,15 +442,15 @@ class PreviewImageActivity :
if (position == null) return
savedPosition = position

val currentFile = previewImagePagerAdapter?.getFileAt(position)
val currentFile = previewMediaPagerAdapter?.getFileAt(position)

if (!isDownloadWorkStarted) {
screenState = PreviewImageActivityState.WaitingForBinder
} else {
if (currentFile != null) {
if (currentFile.isEncrypted &&
!currentFile.isDown &&
previewImagePagerAdapter?.pendingErrorAt(position) == false
previewMediaPagerAdapter?.pendingErrorAt(position) == false
) {
requestForDownload(currentFile)
}
Expand Down Expand Up @@ -507,13 +511,15 @@ class PreviewImageActivity :
get() = supportActionBar == null || supportActionBar?.isShowing == true

fun toggleFullScreen() {
fullScreenAnchorView?.let {
val visible = (it.systemUiVisibility and View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) == 0
if (visible) {
hideSystemUI(it)
} else {
showSystemUI(it)
}
val rootInsets = ViewCompat.getRootWindowInsets(window.decorView) ?: return

// the content is laid out edge to edge so that showing and hiding the bars does not move it
WindowCompat.setDecorFitsSystemWindows(window, false)

if (rootInsets.isVisible(WindowInsetsCompat.Type.systemBars())) {
windowInsetsController.hide(WindowInsetsCompat.Type.systemBars())
} else {
windowInsetsController.show(WindowInsetsCompat.Type.systemBars())
}
}

Expand Down Expand Up @@ -542,27 +548,6 @@ class PreviewImageActivity :
override fun onBrowsedDownTo(folder: OCFile) = Unit
override fun onTransferStateChanged(file: OCFile, downloading: Boolean, uploading: Boolean) = Unit

@Suppress("DEPRECATION")
private fun hideSystemUI(anchorView: View) {
anchorView.systemUiVisibility = (
View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
or View.SYSTEM_UI_FLAG_FULLSCREEN
or View.SYSTEM_UI_FLAG_IMMERSIVE
or View.SYSTEM_UI_FLAG_LAYOUT_STABLE
or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
)
}

@Suppress("DEPRECATION")
private fun showSystemUI(anchorView: View) {
anchorView.systemUiVisibility = (
View.SYSTEM_UI_FLAG_LAYOUT_STABLE
or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
)
}

companion object {
val TAG: String = PreviewImageActivity::class.java.simpleName
const val EXTRA_VIRTUAL_TYPE: String = "EXTRA_VIRTUAL_TYPE"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ class PreviewImageFragment :

hideActionBar()

val mediaFragment: Fragment = newInstance(file, accountManager.user, 0, true, true)
val mediaFragment: Fragment = newInstance(file, accountManager.user, autoplay = true, isLivePhoto = true)
val fragmentManager = requireActivity().supportFragmentManager
fragmentManager.beginTransaction().run {
replace(R.id.top, mediaFragment)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ import com.nextcloud.ui.fileactions.FileActionsBottomSheet.Companion.newInstance
import com.nextcloud.ui.fileactions.FileActionsBottomSheet.ResultListener
import com.nextcloud.utils.extensions.getParcelableArgument
import com.nextcloud.utils.extensions.logFileSize
import com.nextcloud.utils.extensions.setFullscreenButton
import com.nextcloud.utils.extensions.setTitleColor
import com.owncloud.android.R
import com.owncloud.android.databinding.ActivityPreviewMediaBinding
Expand Down Expand Up @@ -488,7 +489,7 @@ class PreviewMediaActivity :
}
)
it.player = videoPlayer
it.setFullscreenButtonClickListener { startFullScreenVideo() }
it.setFullscreenButton(isFullscreen = false) { startFullScreenVideo() }
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/*
* Nextcloud - Android Client
*
* SPDX-FileCopyrightText: 2026 Alper Ozturk <alper.ozturk@nextcloud.com>
* SPDX-FileCopyrightText: 2023 TSI-mc
* SPDX-FileCopyrightText: 2023 Parneet Singh <gurayaparneet@gmail.com>
* SPDX-FileCopyrightText: 2020 Andy Scherzinger <info@andy-scherzinger.de>
Expand All @@ -27,17 +28,13 @@ import android.view.MotionEvent
import android.view.View
import android.view.View.OnTouchListener
import android.view.ViewGroup
import android.widget.FrameLayout
import androidx.annotation.OptIn
import androidx.annotation.StringRes
import androidx.core.net.toUri
import androidx.core.view.MenuHost
import androidx.core.view.MenuProvider
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.marginBottom
import androidx.core.view.updateLayoutParams
import androidx.core.view.updatePadding
import androidx.drawerlayout.widget.DrawerLayout
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
Expand All @@ -46,7 +43,6 @@ import androidx.media3.common.Player
import androidx.media3.common.util.UnstableApi
import androidx.media3.exoplayer.ExoPlayer
import androidx.media3.session.MediaSession
import androidx.media3.ui.DefaultTimeBar
import com.nextcloud.client.account.User
import com.nextcloud.client.account.UserAccountManager
import com.nextcloud.client.di.Injectable
Expand All @@ -60,8 +56,10 @@ import com.nextcloud.client.network.ClientFactory.CreationException
import com.nextcloud.common.NextcloudClient
import com.nextcloud.ui.fileactions.FileAction
import com.nextcloud.ui.fileactions.FileActionsBottomSheet.Companion.newInstance
import com.nextcloud.utils.extensions.applyControlsInsets
import com.nextcloud.utils.extensions.getParcelableArgument
import com.nextcloud.utils.extensions.getTypedActivity
import com.nextcloud.utils.extensions.setFullscreenButton
import com.owncloud.android.R
import com.owncloud.android.databinding.FragmentPreviewMediaBinding
import com.owncloud.android.datamodel.OCFile
Expand Down Expand Up @@ -192,24 +190,11 @@ class PreviewMediaFragment :
private fun applyWindowInsets() {
binding.root.post {
val rootInsets = ViewCompat.getRootWindowInsets(binding.root) ?: return@post
val insets = rootInsets.getInsets(
WindowInsetsCompat.Type.systemBars() or
WindowInsetsCompat.Type.displayCutout()
binding.exoplayerView.applyControlsInsets(
rootInsets.getInsets(
WindowInsetsCompat.Type.systemBars() or WindowInsetsCompat.Type.displayCutout()
)
)
val playerView = binding.exoplayerView
val exoControls = playerView
.findViewById<FrameLayout>(androidx.media3.ui.R.id.exo_bottom_bar)
val exoProgress = playerView
.findViewById<DefaultTimeBar>(androidx.media3.ui.R.id.exo_progress)
val progressOriginalMargin = exoProgress?.marginBottom ?: 0
exoControls?.updateLayoutParams<ViewGroup.MarginLayoutParams> {
bottomMargin = insets.bottom
}
exoProgress?.updateLayoutParams<ViewGroup.MarginLayoutParams> {
bottomMargin = insets.bottom + progressOriginalMargin
}
exoControls?.updatePadding(left = insets.left, right = insets.right)
exoProgress?.updatePadding(left = insets.left, right = insets.right)
}
}

Expand Down Expand Up @@ -324,7 +309,7 @@ class PreviewMediaFragment :
setShowNextButton(false)
setShowPreviousButton(false)
player = exoPlayer
setFullscreenButtonClickListener { startFullScreenVideo() }
setFullscreenButton(isFullscreen = false) { startFullScreenVideo() }
}
}

Expand Down Expand Up @@ -598,22 +583,12 @@ class PreviewMediaFragment :
private const val AUTOPLAY = "AUTOPLAY"
private const val IS_LIVE_PHOTO = "IS_LIVE_PHOTO"

/**
* Creates a fragment to preview a file.
*
*
* When 'fileToDetail' or 'user' are null
*
* @param fileToDetail An [OCFile] to preview in the fragment
* @param user Currently active user
*/
@JvmStatic
fun newInstance(
fileToDetail: OCFile?,
user: User?,
startPlaybackPosition: Long,
autoplay: Boolean,
isLivePhoto: Boolean
startPlaybackPosition: Long = 0,
autoplay: Boolean = false,
isLivePhoto: Boolean = false
): PreviewMediaFragment = PreviewMediaFragment().apply {
arguments = Bundle().apply {
putParcelable(FILE, fileToDetail)
Expand All @@ -624,14 +599,7 @@ class PreviewMediaFragment :
}
}

/**
* Helper method to test if an [OCFile] can be passed to a [PreviewMediaFragment] to be previewed.
*
* @param file File to test if can be previewed.
* @return 'True' if the file can be handled by the fragment.
*/
@JvmStatic
fun canBePreviewed(file: OCFile?): Boolean =
fun isAudioOrVideo(file: OCFile?): Boolean =
file != null && (MimeTypeUtil.isAudio(file) || MimeTypeUtil.isVideo(file))
}
}
Loading
Loading