Background file viewer playlist creation
This commit is contained in:
parent
07f5f8b5d9
commit
f901495e41
@ -14,6 +14,8 @@ import androidx.lifecycle.lifecycleScope
|
|||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.isActive
|
import kotlinx.coroutines.isActive
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.sync.Mutex
|
||||||
|
import kotlinx.coroutines.sync.withLock
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import sushi.hardcore.droidfs.BaseActivity
|
import sushi.hardcore.droidfs.BaseActivity
|
||||||
import sushi.hardcore.droidfs.FileTypes
|
import sushi.hardcore.droidfs.FileTypes
|
||||||
@ -21,7 +23,6 @@ import sushi.hardcore.droidfs.R
|
|||||||
import sushi.hardcore.droidfs.VolumeManagerApp
|
import sushi.hardcore.droidfs.VolumeManagerApp
|
||||||
import sushi.hardcore.droidfs.explorers.ExplorerElement
|
import sushi.hardcore.droidfs.explorers.ExplorerElement
|
||||||
import sushi.hardcore.droidfs.filesystems.EncryptedVolume
|
import sushi.hardcore.droidfs.filesystems.EncryptedVolume
|
||||||
import sushi.hardcore.droidfs.util.IntentUtils
|
|
||||||
import sushi.hardcore.droidfs.util.PathUtils
|
import sushi.hardcore.droidfs.util.PathUtils
|
||||||
import sushi.hardcore.droidfs.util.finishOnClose
|
import sushi.hardcore.droidfs.util.finishOnClose
|
||||||
import sushi.hardcore.droidfs.widgets.CustomAlertDialogBuilder
|
import sushi.hardcore.droidfs.widgets.CustomAlertDialogBuilder
|
||||||
@ -32,9 +33,8 @@ abstract class FileViewerActivity: BaseActivity() {
|
|||||||
private lateinit var originalParentPath: String
|
private lateinit var originalParentPath: String
|
||||||
private lateinit var windowInsetsController: WindowInsetsControllerCompat
|
private lateinit var windowInsetsController: WindowInsetsControllerCompat
|
||||||
private var windowTypeMask = 0
|
private var windowTypeMask = 0
|
||||||
private var foldersFirst = true
|
protected val playlist = mutableListOf<ExplorerElement>()
|
||||||
private var wasMapped = false
|
private val playlistMutex = Mutex()
|
||||||
protected val mappedPlaylist = mutableListOf<ExplorerElement>()
|
|
||||||
protected var currentPlaylistIndex = -1
|
protected var currentPlaylistIndex = -1
|
||||||
private val isLegacyFullscreen = Build.VERSION.SDK_INT <= Build.VERSION_CODES.R
|
private val isLegacyFullscreen = Build.VERSION.SDK_INT <= Build.VERSION_CODES.R
|
||||||
|
|
||||||
@ -46,7 +46,6 @@ abstract class FileViewerActivity: BaseActivity() {
|
|||||||
intent.getIntExtra("volumeId", -1)
|
intent.getIntExtra("volumeId", -1)
|
||||||
)!!
|
)!!
|
||||||
finishOnClose(encryptedVolume)
|
finishOnClose(encryptedVolume)
|
||||||
foldersFirst = sharedPrefs.getBoolean("folders_first", true)
|
|
||||||
windowInsetsController = WindowInsetsControllerCompat(window, window.decorView)
|
windowInsetsController = WindowInsetsControllerCompat(window, window.decorView)
|
||||||
windowInsetsController.addOnControllableInsetsChangedListener { _, typeMask ->
|
windowInsetsController.addOnControllableInsetsChangedListener { _, typeMask ->
|
||||||
windowTypeMask = typeMask
|
windowTypeMask = typeMask
|
||||||
@ -131,48 +130,53 @@ abstract class FileViewerActivity: BaseActivity() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected fun createPlaylist() {
|
protected suspend fun createPlaylist() {
|
||||||
if (!wasMapped){
|
playlistMutex.withLock {
|
||||||
encryptedVolume.recursiveMapFiles(originalParentPath)?.let { elements ->
|
if (currentPlaylistIndex != -1) {
|
||||||
for (e in elements) {
|
// playlist already initialized
|
||||||
if (e.isRegularFile) {
|
return
|
||||||
if (FileTypes.isExtensionType(getFileType(), e.name) || filePath == e.fullPath) {
|
|
||||||
mappedPlaylist.add(e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
withContext(Dispatchers.IO) {
|
||||||
|
encryptedVolume.recursiveMapFiles(originalParentPath)?.filterTo(playlist) { e ->
|
||||||
|
e.isRegularFile && (FileTypes.isExtensionType(getFileType(), e.name) || filePath == e.fullPath)
|
||||||
}
|
}
|
||||||
val sortOrder = intent.getStringExtra("sortOrder") ?: "name"
|
val sortOrder = intent.getStringExtra("sortOrder") ?: "name"
|
||||||
ExplorerElement.sortBy(sortOrder, foldersFirst, mappedPlaylist)
|
val foldersFirst = sharedPrefs.getBoolean("folders_first", true)
|
||||||
//find current index
|
ExplorerElement.sortBy(sortOrder, foldersFirst, playlist)
|
||||||
for ((i, e) in mappedPlaylist.withIndex()){
|
currentPlaylistIndex = playlist.indexOfFirst { it.fullPath == filePath }
|
||||||
if (filePath == e.fullPath){
|
|
||||||
currentPlaylistIndex = i
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
wasMapped = true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected fun playlistNext(forward: Boolean) {
|
private fun updateCurrentItem() {
|
||||||
|
filePath = playlist[currentPlaylistIndex].fullPath
|
||||||
|
}
|
||||||
|
|
||||||
|
protected suspend fun playlistNext(forward: Boolean) {
|
||||||
createPlaylist()
|
createPlaylist()
|
||||||
currentPlaylistIndex = if (forward) {
|
currentPlaylistIndex = if (forward) {
|
||||||
(currentPlaylistIndex+1)%mappedPlaylist.size
|
(currentPlaylistIndex + 1).mod(playlist.size)
|
||||||
} else {
|
} else {
|
||||||
var x = (currentPlaylistIndex-1)%mappedPlaylist.size
|
(currentPlaylistIndex - 1).mod(playlist.size)
|
||||||
if (x < 0) {
|
|
||||||
x += mappedPlaylist.size
|
|
||||||
}
|
}
|
||||||
x
|
updateCurrentItem()
|
||||||
}
|
|
||||||
filePath = mappedPlaylist[currentPlaylistIndex].fullPath
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected fun refreshPlaylist() {
|
protected suspend fun deleteCurrentFile(): Boolean {
|
||||||
mappedPlaylist.clear()
|
createPlaylist() // ensure we know the current position in the playlist
|
||||||
wasMapped = false
|
return if (encryptedVolume.deleteFile(filePath)) {
|
||||||
createPlaylist()
|
playlist.removeAt(currentPlaylistIndex)
|
||||||
|
if (playlist.size != 0) {
|
||||||
|
if (currentPlaylistIndex == playlist.size) {
|
||||||
|
// deleted the last element of the playlist, go back to the first
|
||||||
|
currentPlaylistIndex = 0
|
||||||
|
}
|
||||||
|
updateCurrentItem()
|
||||||
|
}
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected fun goBackToExplorer() {
|
protected fun goBackToExplorer() {
|
||||||
|
@ -12,10 +12,12 @@ import android.widget.Toast
|
|||||||
import androidx.activity.addCallback
|
import androidx.activity.addCallback
|
||||||
import androidx.activity.viewModels
|
import androidx.activity.viewModels
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
|
import androidx.lifecycle.lifecycleScope
|
||||||
import com.bumptech.glide.Glide
|
import com.bumptech.glide.Glide
|
||||||
import com.bumptech.glide.RequestBuilder
|
import com.bumptech.glide.RequestBuilder
|
||||||
import com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool
|
import com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool
|
||||||
import com.bumptech.glide.load.resource.bitmap.BitmapTransformation
|
import com.bumptech.glide.load.resource.bitmap.BitmapTransformation
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
import sushi.hardcore.droidfs.Constants
|
import sushi.hardcore.droidfs.Constants
|
||||||
import sushi.hardcore.droidfs.R
|
import sushi.hardcore.droidfs.R
|
||||||
import sushi.hardcore.droidfs.databinding.ActivityImageViewerBinding
|
import sushi.hardcore.droidfs.databinding.ActivityImageViewerBinding
|
||||||
@ -105,17 +107,15 @@ class ImageViewer: FileViewerActivity() {
|
|||||||
.keepFullScreen()
|
.keepFullScreen()
|
||||||
.setTitle(R.string.warning)
|
.setTitle(R.string.warning)
|
||||||
.setPositiveButton(R.string.ok) { _, _ ->
|
.setPositiveButton(R.string.ok) { _, _ ->
|
||||||
createPlaylist() //be sure the playlist is created before deleting if there is only one image
|
lifecycleScope.launch {
|
||||||
if (encryptedVolume.deleteFile(filePath)) {
|
if (deleteCurrentFile()) {
|
||||||
playlistNext(true)
|
if (playlist.size == 0) { // no more image left
|
||||||
refreshPlaylist()
|
|
||||||
if (mappedPlaylist.size == 0) { //deleted all images of the playlist
|
|
||||||
goBackToExplorer()
|
goBackToExplorer()
|
||||||
} else {
|
} else {
|
||||||
loadImage(true)
|
loadImage(true)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
CustomAlertDialogBuilder(this, theme)
|
CustomAlertDialogBuilder(this@ImageViewer, theme)
|
||||||
.keepFullScreen()
|
.keepFullScreen()
|
||||||
.setTitle(R.string.error)
|
.setTitle(R.string.error)
|
||||||
.setMessage(getString(R.string.remove_failed, fileName))
|
.setMessage(getString(R.string.remove_failed, fileName))
|
||||||
@ -123,6 +123,7 @@ class ImageViewer: FileViewerActivity() {
|
|||||||
.show()
|
.show()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
.setNegativeButton(R.string.cancel, null)
|
.setNegativeButton(R.string.cancel, null)
|
||||||
.setMessage(getString(R.string.single_delete_confirm, fileName))
|
.setMessage(getString(R.string.single_delete_confirm, fileName))
|
||||||
.show()
|
.show()
|
||||||
@ -199,6 +200,7 @@ class ImageViewer: FileViewerActivity() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun swipeImage(deltaX: Float, slideshowSwipe: Boolean = false) {
|
private fun swipeImage(deltaX: Float, slideshowSwipe: Boolean = false) {
|
||||||
|
lifecycleScope.launch {
|
||||||
playlistNext(deltaX < 0)
|
playlistNext(deltaX < 0)
|
||||||
loadImage(true)
|
loadImage(true)
|
||||||
if (slideshowActive) {
|
if (slideshowActive) {
|
||||||
@ -208,6 +210,7 @@ class ImageViewer: FileViewerActivity() {
|
|||||||
handler.postDelayed(slideshowNext, Constants.SLIDESHOW_DELAY)
|
handler.postDelayed(slideshowNext, Constants.SLIDESHOW_DELAY)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun stopSlideshow(){
|
private fun stopSlideshow(){
|
||||||
slideshowActive = false
|
slideshowActive = false
|
||||||
|
@ -2,6 +2,7 @@ package sushi.hardcore.droidfs.file_viewers
|
|||||||
|
|
||||||
import android.view.WindowManager
|
import android.view.WindowManager
|
||||||
import androidx.annotation.OptIn
|
import androidx.annotation.OptIn
|
||||||
|
import androidx.lifecycle.lifecycleScope
|
||||||
import androidx.media3.common.MediaItem
|
import androidx.media3.common.MediaItem
|
||||||
import androidx.media3.common.PlaybackException
|
import androidx.media3.common.PlaybackException
|
||||||
import androidx.media3.common.Player
|
import androidx.media3.common.Player
|
||||||
@ -11,6 +12,7 @@ import androidx.media3.exoplayer.ExoPlayer
|
|||||||
import androidx.media3.exoplayer.source.MediaSource
|
import androidx.media3.exoplayer.source.MediaSource
|
||||||
import androidx.media3.exoplayer.source.ProgressiveMediaSource
|
import androidx.media3.exoplayer.source.ProgressiveMediaSource
|
||||||
import androidx.media3.extractor.DefaultExtractorsFactory
|
import androidx.media3.extractor.DefaultExtractorsFactory
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
import sushi.hardcore.droidfs.Constants
|
import sushi.hardcore.droidfs.Constants
|
||||||
import sushi.hardcore.droidfs.R
|
import sushi.hardcore.droidfs.R
|
||||||
import sushi.hardcore.droidfs.widgets.CustomAlertDialogBuilder
|
import sushi.hardcore.droidfs.widgets.CustomAlertDialogBuilder
|
||||||
@ -39,12 +41,16 @@ abstract class MediaPlayer: FileViewerActivity() {
|
|||||||
private fun initializePlayer(){
|
private fun initializePlayer(){
|
||||||
player = ExoPlayer.Builder(this).setSeekForwardIncrementMs(5000).build()
|
player = ExoPlayer.Builder(this).setSeekForwardIncrementMs(5000).build()
|
||||||
bindPlayer(player)
|
bindPlayer(player)
|
||||||
|
player.addMediaSource(createMediaSource(filePath))
|
||||||
|
lifecycleScope.launch {
|
||||||
createPlaylist()
|
createPlaylist()
|
||||||
for (e in mappedPlaylist) {
|
playlist.forEachIndexed { index, e ->
|
||||||
player.addMediaSource(createMediaSource(e.fullPath))
|
if (index != currentPlaylistIndex) {
|
||||||
|
player.addMediaSource(index, createMediaSource(e.fullPath))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
player.repeatMode = Player.REPEAT_MODE_ALL
|
player.repeatMode = Player.REPEAT_MODE_ALL
|
||||||
player.seekToDefaultPosition(currentPlaylistIndex)
|
|
||||||
player.playWhenReady = true
|
player.playWhenReady = true
|
||||||
player.addListener(object : Player.Listener{
|
player.addListener(object : Player.Listener{
|
||||||
override fun onVideoSizeChanged(videoSize: VideoSize) {
|
override fun onVideoSizeChanged(videoSize: VideoSize) {
|
||||||
@ -67,11 +73,13 @@ abstract class MediaPlayer: FileViewerActivity() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onMediaItemTransition(mediaItem: MediaItem?, reason: Int) {
|
override fun onMediaItemTransition(mediaItem: MediaItem?, reason: Int) {
|
||||||
if (player.repeatMode != Player.REPEAT_MODE_ONE) {
|
if (player.repeatMode != Player.REPEAT_MODE_ONE && currentPlaylistIndex != -1) {
|
||||||
playlistNext(player.currentMediaItemIndex == (currentPlaylistIndex + 1) % mappedPlaylist.size)
|
lifecycleScope.launch {
|
||||||
|
playlistNext(player.currentMediaItemIndex == (currentPlaylistIndex + 1) % player.mediaItemCount)
|
||||||
refreshFileName()
|
refreshFileName()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
player.prepare()
|
player.prepare()
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user