Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
73 commits
Select commit Hold shift + click to select a range
5ed75b9
feat: Move to new MediaPlayer and let the player play the media, even…
tevincent Jul 25, 2024
caf3f32
feat: Add media3 title
sirambd Aug 6, 2024
75ad28d
chore: Move class variable
tevincent Aug 6, 2024
9841223
feat: Save media position
tevincent Oct 8, 2024
172ea6c
chore: Remove onFragmentSelected and use onResume instead
tevincent Oct 8, 2024
2c50a6c
chore: Clean code
tevincent Oct 8, 2024
6a10f14
fix: Pause player when we unselect a fragment
tevincent Oct 8, 2024
b18dffb
chore: Clean code
tevincent Oct 8, 2024
d49e5bb
chore: Create a specific package for playback related stuff
tevincent Oct 10, 2024
cb7cfb1
chore: Merge Music and Video fragment to only one
tevincent Oct 10, 2024
d6c3067
chore: Remove unused import
tevincent Oct 17, 2024
cad14a8
fix: Play back from where it was paused
tevincent Oct 21, 2024
d67f3a1
fix: Use only one instance of MediaController
tevincent Feb 5, 2025
dc6f9b6
fix: Add preview (to notification and player)
tevincent Feb 7, 2025
2b47d37
fix: Avoid starting PIP when navigating back with a media playing
tevincent Feb 7, 2025
06a4000
chore: Clean code
tevincent Feb 10, 2025
b6d9a73
chore: Fix rebase
tevincent Mar 7, 2025
205e749
chore: Remove space
tevincent Mar 10, 2025
8bfbddc
chore: Modify comment
tevincent Mar 10, 2025
e2bd169
chore: Use toUri extension
tevincent Mar 10, 2025
3b4ebce
feat: Add the subtitle button
tevincent Mar 25, 2025
a404c1f
fix: Release what need to be released to avoid weird issues with the …
tevincent Mar 26, 2025
474568e
chore: Code review
tevincent Mar 28, 2025
fb8a35c
chore: Reuse mainExecutor
tevincent Mar 28, 2025
1a02981
chore: Remove printStackTrace
tevincent Mar 28, 2025
16014a4
chore: Code review
tevincent Mar 28, 2025
cad5455
fix: Check if player is not null before trying to release it in Service
tevincent Mar 28, 2025
c201bd9
fix: Brought app to foreground if we click on the media notification
tevincent Mar 28, 2025
6f04fc5
fix: Toggle fullscreen mode to avoid having the topAPpBar displayed i…
tevincent Mar 28, 2025
536b2f2
fix: Do not start PIP mode if the media is a music
tevincent Mar 28, 2025
eab2812
chore: Clean the way we check if we can start PIP mode
tevincent Mar 28, 2025
21bef44
chore: Use setMediaItem with position directly instead of seekTo
tevincent Mar 28, 2025
3e8fd45
feat: Display the video inside an Activity to handle the PIP mode mor…
tevincent Apr 9, 2025
2fc39e2
chore: Clean code
tevincent Apr 15, 2025
4aa5c5d
chore: Fix conflicts
tevincent May 13, 2025
560d493
fix: Remove MediaController use for videos because we don't want any …
tevincent May 13, 2025
69a1090
chore: Fix rebase
tevincent May 16, 2025
678427f
chore: Remove unused variable
tevincent May 16, 2025
cecd89b
chore: Add exoplayer dependencies to the new build.gradle.kts
tevincent Jun 3, 2025
1390bc6
chore: Clean VideoActivity
tevincent Jun 3, 2025
c884d01
chore: Update comment because it's not only for videos
tevincent Jun 3, 2025
32b8df2
chore: Clean PlaybackUtils
tevincent Jun 3, 2025
7741980
chore: Use a lazy for UserDrive
tevincent Jun 3, 2025
f276795
chore: Use a list of resID of exoPlayer UI elements to hide
tevincent Jun 3, 2025
953ec20
chore: Show player controller directly when we start the VideoActivity
tevincent Jun 3, 2025
8880b8a
chore: Refactor getCurrentFile method
tevincent Jun 3, 2025
4feb519
Merge branch 'main' into player-background-rebased
tevincent Jul 17, 2025
c02fd7b
chore: Fix merge
tevincent Jul 17, 2025
75ddcce
chore: Update Core
tevincent Jul 17, 2025
25c91a7
chore: Improve code
tevincent Jul 17, 2025
4562ca1
chore: Fix error during merge
tevincent Jul 22, 2025
ac5a7d7
Merge branch 'main' into player-background-rebased
tevincent Aug 8, 2025
37ce91b
Merge branch 'main' into player-background-rebased
tevincent Aug 11, 2025
e28f170
chore: Update Core
tevincent Aug 11, 2025
e374da3
Merge branch 'main' into player-background-rebased
tevincent Aug 22, 2025
ff4a386
chore: Activate "exclude from recent" only when "Don't keep Activitie…
tevincent Aug 26, 2025
bb6a1cf
Merge branch 'main' into player-background-rebased
KevinBoulongne Sep 3, 2025
12956d3
chore: Bump Media3.ExoPlayer to 1.8.0
KevinBoulongne Sep 3, 2025
8ac9a61
Merge branch 'main' into player-background-rebased
tevincent Sep 16, 2025
cbf62a0
Merge branch 'main' into player-background-rebased
tevincent Sep 19, 2025
69f4171
Merge branch 'main' into player-background-rebased
tevincent Sep 22, 2025
a1ad1b8
chore: Remove unused import
tevincent Sep 22, 2025
982428c
Merge branch 'main' into player-background-rebased
tevincent Sep 22, 2025
beb6f3c
chore: Code review
tevincent Sep 22, 2025
8bb7ba6
chore: Don't use not()
tevincent Sep 22, 2025
8d6ce5f
chore: Move don't keep activities to Core
tevincent Sep 22, 2025
ebe5042
Merge branch 'main' into player-background-rebased
tevincent Oct 8, 2025
41ad7ad
chore: Fix merge
tevincent Oct 8, 2025
46b98b8
Merge branch 'main' into player-background-rebased
tevincent Nov 6, 2025
1185155
chore: Remove LocalBroadcast
tevincent Nov 6, 2025
7787086
Merge branch 'main' into player-background-rebased
tevincent Dec 30, 2025
8191547
chore: Update Core
tevincent Dec 30, 2025
ddecde7
chore: Use auth module instead of legacy
tevincent Dec 30, 2025
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
7 changes: 4 additions & 3 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -214,9 +214,10 @@ dependencies {
ksp(core.hilt.androidx.compiler)

implementation(libs.exoplayer)
implementation(libs.exoplayer.core)
implementation(libs.exoplayer.ui)
implementation(libs.extension.okhttp)
implementation(libs.exoplayer.dash)
implementation(libs.exoplayer.media3.ui)
implementation(libs.exoplayer.media3.datasource)
implementation(libs.exoplayer.media3.session)

implementation(libs.android.pdfview)
implementation(libs.gravity.snap.helper)
Expand Down
16 changes: 16 additions & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
android:name="android.permission.READ_MEDIA_VISUAL_USER_SELECTED"
android:minSdkVersion="34" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />

Expand Down Expand Up @@ -103,11 +104,26 @@
android:name=".ui.MainActivity"
android:configChanges="orientation|screenSize|layoutDirection|screenLayout" />

<activity
android:name=".ui.fileList.preview.playback.VideoActivity"
android:configChanges="orientation|screenSize|layoutDirection|screenLayout|smallestScreenSize"
android:launchMode="singleInstance"
android:supportsPictureInPicture="true" />

<service
android:name="androidx.work.impl.foreground.SystemForegroundService"
android:foregroundServiceType="dataSync"
tools:node="merge" />

<service
android:name=".ui.fileList.preview.playback.PlaybackService"
android:exported="false"
android:foregroundServiceType="mediaPlayback">
<intent-filter>
<action android:name="androidx.media3.session.MediaSessionService" />
</intent-filter>
</service>

<activity android:name=".ui.menu.settings.SyncSettingsActivity" />

<activity android:name=".ui.MaintenanceActivity" />
Expand Down
1 change: 1 addition & 0 deletions app/src/main/java/com/infomaniak/drive/MainApplication.kt
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ open class MainApplication : Application(), SingletonImageLoader.Factory, Defaul
}

var geniusScanIsReady = false
var isVideoActivityInPIPMode = false

private val appUpdateWorkerScheduler by lazy { AppUpdateScheduler(applicationContext) }

Expand Down
1 change: 1 addition & 0 deletions app/src/main/java/com/infomaniak/drive/data/models/File.kt
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,7 @@ open class File(
}

fun isPDF() = getFileType() == ExtensionType.PDF
fun isVideo() = getFileType() == ExtensionType.VIDEO

fun getFileType(): ExtensionType {
return if (isFromUploads) getFileTypeFromExtension() else when (extensionType) {
Expand Down
109 changes: 76 additions & 33 deletions app/src/main/java/com/infomaniak/drive/ui/BasePreviewSliderFragment.kt
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,13 @@ import android.os.Bundle
import android.view.View
import androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult
import androidx.annotation.CallSuper
import androidx.annotation.OptIn
import androidx.core.view.isGone
import androidx.core.view.isVisible
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.lifecycle.lifecycleScope
import androidx.media3.common.util.UnstableApi
import androidx.navigation.fragment.findNavController
import androidx.transition.TransitionManager
import androidx.viewpager2.widget.ViewPager2
Expand All @@ -47,6 +49,8 @@ import com.infomaniak.drive.ui.fileList.preview.PreviewPDFFragment
import com.infomaniak.drive.ui.fileList.preview.PreviewPDFHandler
import com.infomaniak.drive.ui.fileList.preview.PreviewSliderAdapter
import com.infomaniak.drive.ui.fileList.preview.PreviewSliderViewModel
import com.infomaniak.drive.ui.fileList.preview.playback.PlaybackUtils
import com.infomaniak.drive.ui.fileList.preview.playback.PreviewPlaybackFragment
import com.infomaniak.drive.utils.DrivePermissions
import com.infomaniak.drive.utils.Utils.openWith
import com.infomaniak.drive.utils.openOnlyOfficeDocument
Expand All @@ -60,6 +64,7 @@ import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch

@OptIn(UnstableApi::class)
abstract class BasePreviewSliderFragment : Fragment(), FileInfoActionsView.OnItemClickListener {

protected var _binding: FragmentPreviewSliderBinding? = null
Expand All @@ -77,8 +82,16 @@ abstract class BasePreviewSliderFragment : Fragment(), FileInfoActionsView.OnIte
private var isOverlayShown = true

override val currentContext by lazy { requireContext() }

private val bottomSheetUpdates = MutableSharedFlow<File>(extraBufferCapacity = 1)

override lateinit var currentFile: File

var positionsForMedia: MutableMap<Int, Long> = mutableMapOf()

// If the user want to navigate back and something is playing, we don't want to start PIP
private var hasNavigateBack = false

// This is not protected, otherwise it won't build because PublicSharePreviewSliderFragment needs it public for the interface
// it implements
val downloadPermissions: DrivePermissions = DrivePermissions(type = DrivePermissions.Type.DownloadingWithDownloadManager)
Expand Down Expand Up @@ -109,7 +122,7 @@ abstract class BasePreviewSliderFragment : Fragment(), FileInfoActionsView.OnIte
header.apply {
setupWindowInsetsListener(root, bottomSheetView) { pdfContainer.setMargins(right = it?.right ?: 0) }
setup(
onBackClicked = findNavController()::popBackStack,
onBackClicked = ::navigateBack,
onOpenWithClicked = ::openWith,
onEditClicked = { openOnlyOfficeDocument(currentFile, mainViewModel.hasNetwork) },
)
Expand All @@ -127,36 +140,7 @@ abstract class BasePreviewSliderFragment : Fragment(), FileInfoActionsView.OnIte
isPublicShared = isPublicShared,
)

viewPager.apply {
adapter = previewSliderAdapter
offscreenPageLimit = 1

registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
override fun onPageSelected(position: Int) {
val file = previewSliderAdapter.getFile(position)
currentFile = file
previewSliderViewModel.currentPreview = file

var shouldDisplayPageNumber = false

childFragmentManager.findFragmentByTag("f${previewSliderAdapter.getItemId(position)}")?.apply {
trackScreen()
shouldDisplayPageNumber = this is PreviewPDFFragment && tryToUpdatePageCount()
}

with(header) {
toggleEditVisibility(isVisible = file.isOnlyOfficePreview())
setPageNumberVisibility(isVisible = shouldDisplayPageNumber)
toggleOpenWithVisibility(isVisible = !isPublicShared && !file.isOnlyOfficePreview())
}

setPrintButtonVisibility(isGone = !file.isPDF() || !canDownloadFiles)

(bottomSheetView as? FileInfoActionsView)?.openWith?.isGone = isPublicShared
bottomSheetUpdates.tryEmit(file)
}
})
}
initViewPager()

previewSliderViewModel.pdfIsDownloading.observe(viewLifecycleOwner) { isDownloading ->
if (!currentFile.isOnlyOfficePreview()) header.toggleOpenWithVisibility(isVisible = !isDownloading)
Expand Down Expand Up @@ -189,6 +173,14 @@ abstract class BasePreviewSliderFragment : Fragment(), FileInfoActionsView.OnIte
}
}

override fun onPictureInPictureModeChanged(isInPictureInPictureMode: Boolean) {
super.onPictureInPictureModeChanged(isInPictureInPictureMode)
if (isInPictureInPictureMode) {
binding.header.toggleVisibility(isVisible = false)
toggleBottomSheet(shouldShow = false)
}
}

override fun onPause() {
super.onPause()
if (noPreviewList()) return
Expand All @@ -200,6 +192,7 @@ abstract class BasePreviewSliderFragment : Fragment(), FileInfoActionsView.OnIte
super.onStop()
}

@OptIn(UnstableApi::class)
override fun onDestroyView() {
super.onDestroyView()
_binding?.previewSliderParent?.let(TransitionManager::endTransitions)
Expand All @@ -213,9 +206,61 @@ abstract class BasePreviewSliderFragment : Fragment(), FileInfoActionsView.OnIte
mainViewModel.currentPreviewFileList = LinkedHashMap()
}

// Release Player
PlaybackUtils.releasePlayer()

super.onDestroy()
}

private fun initViewPager() = with(binding) {
viewPager.apply {
adapter = previewSliderAdapter
offscreenPageLimit = 1

registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {

@OptIn(UnstableApi::class)
override fun onPageSelected(position: Int) {

val file = previewSliderAdapter.getFile(position)
currentFile = file
previewSliderViewModel.currentPreview = file

var shouldDisplayPageNumber = false

val selectedFragment =
childFragmentManager.findFragmentByTag("f${previewSliderAdapter.getItemId(position)}")?.apply {
this.trackScreen()
shouldDisplayPageNumber = this is PreviewPDFFragment && tryToUpdatePageCount()
}

// Implementation of onFragmentUnselected to handle resume of media to the same position, only
// for PreviewPlaybackFragment.
childFragmentManager.fragments.filterIsInstance<PreviewPlaybackFragment>().forEach { fragment ->
if (fragment != selectedFragment) {
fragment.onFragmentUnselected()
}
}

with(header) {
toggleEditVisibility(isVisible = currentFile.isOnlyOfficePreview())
setPageNumberVisibility(isVisible = shouldDisplayPageNumber)
toggleOpenWithVisibility(isVisible = !isPublicShared && !currentFile.isOnlyOfficePreview())
}

setPrintButtonVisibility(isGone = !file.isPDF() || !canDownloadFiles)
(bottomSheetView as? FileInfoActionsView)?.openWith?.isGone = isPublicShared
bottomSheetUpdates.tryEmit(file)
}
})
}
}

private fun navigateBack() {
hasNavigateBack = true
findNavController().popBackStack()
}

protected fun noPreviewList() = mainViewModel.currentPreviewFileList.isEmpty()

protected open fun setBackActionHandlers() {
Expand Down Expand Up @@ -244,8 +289,6 @@ abstract class BasePreviewSliderFragment : Fragment(), FileInfoActionsView.OnIte
}
}

private val bottomSheetUpdates = MutableSharedFlow<File>(extraBufferCapacity = 1)

private fun clearEdgeToEdge() = with(requireActivity()) {
toggleSystemBar(true)
}
Expand Down
20 changes: 20 additions & 0 deletions app/src/main/java/com/infomaniak/drive/ui/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ import android.annotation.SuppressLint
import android.app.Dialog
import android.content.ContentResolver
import android.content.Context
import android.content.Intent
import android.content.Intent.FLAG_ACTIVITY_REORDER_TO_FRONT
import android.graphics.Bitmap
import android.graphics.Canvas
import android.graphics.Paint
Expand All @@ -40,6 +42,7 @@ import androidx.activity.result.IntentSenderRequest
import androidx.activity.result.contract.ActivityResultContracts
import androidx.activity.result.contract.ActivityResultContracts.StartIntentSenderForResult
import androidx.activity.viewModels
import androidx.annotation.OptIn
import androidx.core.content.ContextCompat
import androidx.core.content.pm.ShortcutManagerCompat
import androidx.core.graphics.drawable.toBitmap
Expand All @@ -49,6 +52,7 @@ import androidx.core.view.isVisible
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.flowWithLifecycle
import androidx.lifecycle.lifecycleScope
import androidx.media3.common.util.UnstableApi
import androidx.navigation.NavController
import androidx.navigation.NavDestination
import androidx.navigation.findNavController
Expand Down Expand Up @@ -76,6 +80,7 @@ import com.infomaniak.core.legacy.utils.whenResultIsOk
import com.infomaniak.core.observe
import com.infomaniak.drive.GeniusScanUtils.scanResultProcessing
import com.infomaniak.drive.GeniusScanUtils.startScanFlow
import com.infomaniak.drive.MainApplication
import com.infomaniak.drive.MatomoDrive.MatomoCategory
import com.infomaniak.drive.MatomoDrive.MatomoName
import com.infomaniak.drive.MatomoDrive.trackAccountEvent
Expand All @@ -101,6 +106,7 @@ import com.infomaniak.drive.extensions.trackDestination
import com.infomaniak.drive.ui.addFiles.AddFileBottomSheetDialogArgs
import com.infomaniak.drive.ui.bottomSheetDialogs.FileInfoActionsBottomSheetDialogArgs
import com.infomaniak.drive.ui.fileList.FileListFragmentArgs
import com.infomaniak.drive.ui.fileList.preview.playback.VideoActivity
import com.infomaniak.drive.utils.AccountUtils
import com.infomaniak.drive.utils.DownloadOfflineFileManager
import com.infomaniak.drive.utils.DrivePermissions
Expand Down Expand Up @@ -427,6 +433,7 @@ class MainActivity : BaseActivity() {
}
//endregion

@OptIn(UnstableApi::class)
override fun onResume() {
super.onResume()

Expand All @@ -438,6 +445,19 @@ class MainActivity : BaseActivity() {

setBottomNavigationUserAvatar(this)
startContentObserverService()

finishPIPActivity()
}

@OptIn(UnstableApi::class)
private fun finishPIPActivity() {
lifecycleScope.launch {
if ((application as MainApplication).isVideoActivityInPIPMode) {
startActivity(Intent(this@MainActivity, VideoActivity::class.java).apply {
flags = FLAG_ACTIVITY_REORDER_TO_FRONT
})
}
}
}

private fun launchNextDeleteRequest() {
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,16 @@
*/
package com.infomaniak.drive.ui.fileList.preview

import androidx.annotation.OptIn
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentManager
import androidx.lifecycle.Lifecycle
import androidx.media3.common.util.UnstableApi
import androidx.viewpager2.adapter.FragmentStateAdapter
import com.infomaniak.drive.data.models.ExtensionType
import com.infomaniak.drive.data.models.File
import com.infomaniak.drive.data.models.UserDrive
import com.infomaniak.drive.ui.fileList.preview.playback.PreviewPlaybackFragment

class PreviewSliderAdapter(
manager: FragmentManager,
Expand All @@ -37,14 +40,14 @@ class PreviewSliderAdapter(

override fun getItemCount() = files.size

@OptIn(UnstableApi::class)
override fun createFragment(position: Int): Fragment {
val file = getFile(position)
val args = PreviewFragmentArgs(fileId = file.id, userDrive = userDrive, isPublicShared = isPublicShared).toBundle()

return when (file.getFileType()) {
ExtensionType.IMAGE -> PreviewPictureFragment()
ExtensionType.VIDEO -> PreviewVideoFragment()
ExtensionType.AUDIO -> PreviewMusicFragment()
ExtensionType.VIDEO, ExtensionType.AUDIO -> PreviewPlaybackFragment()
ExtensionType.PDF -> PreviewPDFFragment()
else -> if (file.isOnlyOfficePreview()) PreviewPDFFragment() else PreviewOtherFragment()
}.apply { arguments = args }
Expand Down
Loading