forked from hardcoresushi/DroidFS
Media playlist
This commit is contained in:
parent
e92804ae34
commit
86e9572431
@ -15,8 +15,8 @@ android {
|
|||||||
applicationId "sushi.hardcore.droidfs"
|
applicationId "sushi.hardcore.droidfs"
|
||||||
minSdkVersion 21
|
minSdkVersion 21
|
||||||
targetSdkVersion 29
|
targetSdkVersion 29
|
||||||
versionCode 11
|
versionCode 12
|
||||||
versionName "1.4.3"
|
versionName "1.4.4"
|
||||||
|
|
||||||
ndk {
|
ndk {
|
||||||
abiFilters 'x86_64', 'armeabi-v7a', 'arm64-v8a'
|
abiFilters 'x86_64', 'armeabi-v7a', 'arm64-v8a'
|
||||||
@ -54,10 +54,12 @@ dependencies {
|
|||||||
implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.1.0"
|
implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.1.0"
|
||||||
implementation "com.jaredrummler:cyanea:1.0.2"
|
implementation "com.jaredrummler:cyanea:1.0.2"
|
||||||
implementation "com.github.bumptech.glide:glide:4.11.0"
|
implementation "com.github.bumptech.glide:glide:4.11.0"
|
||||||
implementation "com.google.android.exoplayer:exoplayer-core:2.11.7"
|
|
||||||
implementation "com.google.android.exoplayer:exoplayer-ui:2.11.7"
|
|
||||||
implementation "androidx.biometric:biometric:1.1.0"
|
implementation "androidx.biometric:biometric:1.1.0"
|
||||||
|
|
||||||
|
def exoplayer_version = "2.13.2"
|
||||||
|
implementation "com.google.android.exoplayer:exoplayer-core:$exoplayer_version"
|
||||||
|
implementation "com.google.android.exoplayer:exoplayer-ui:$exoplayer_version"
|
||||||
|
|
||||||
def camerax_version = "1.1.0-alpha01"
|
def camerax_version = "1.1.0-alpha01"
|
||||||
implementation "androidx.camera:camera-camera2:$camerax_version"
|
implementation "androidx.camera:camera-camera2:$camerax_version"
|
||||||
implementation "androidx.camera:camera-lifecycle:$camerax_version"
|
implementation "androidx.camera:camera-lifecycle:$camerax_version"
|
||||||
|
@ -21,7 +21,7 @@ class ConstValues {
|
|||||||
Pair("text", listOf("txt", "json", "conf", "log", "xml", "java", "kt", "py", "pl", "rb", "go", "c", "h", "cpp", "hpp", "sh", "bat", "js", "html", "css", "php", "yml", "yaml", "ini", "md"))
|
Pair("text", listOf("txt", "json", "conf", "log", "xml", "java", "kt", "py", "pl", "rb", "go", "c", "h", "cpp", "hpp", "sh", "bat", "js", "html", "css", "php", "yml", "yaml", "ini", "md"))
|
||||||
)
|
)
|
||||||
|
|
||||||
private fun isExtensionType(extensionType: String, path: String): Boolean {
|
fun isExtensionType(extensionType: String, path: String): Boolean {
|
||||||
return fileExtensions[extensionType]?.contains(File(path).extension.toLowerCase(Locale.ROOT)) ?: false
|
return fileExtensions[extensionType]?.contains(File(path).extension.toLowerCase(Locale.ROOT)) ?: false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -112,12 +112,11 @@ open class BaseExplorerActivity : BaseActivity() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun startFileViewer(cls: Class<*>, filePath: String, sortOrder: String = ""){
|
private fun startFileViewer(cls: Class<*>, filePath: String){
|
||||||
val intent = Intent(this, cls)
|
val intent = Intent(this, cls).apply {
|
||||||
intent.putExtra("path", filePath)
|
putExtra("path", filePath)
|
||||||
intent.putExtra("sessionID", gocryptfsVolume.sessionID)
|
putExtra("sessionID", gocryptfsVolume.sessionID)
|
||||||
if (sortOrder.isNotEmpty()){
|
putExtra("sortOrder", sortOrderValues[currentSortOrderIndex])
|
||||||
intent.putExtra("sortOrder", sortOrder)
|
|
||||||
}
|
}
|
||||||
isStartingActivity = true
|
isStartingActivity = true
|
||||||
startActivity(intent)
|
startActivity(intent)
|
||||||
@ -142,7 +141,7 @@ open class BaseExplorerActivity : BaseActivity() {
|
|||||||
setCurrentPath(PathUtils.getParentPath(currentDirectoryPath))
|
setCurrentPath(PathUtils.getParentPath(currentDirectoryPath))
|
||||||
}
|
}
|
||||||
isImage(fullPath) -> {
|
isImage(fullPath) -> {
|
||||||
startFileViewer(ImageViewer::class.java, fullPath, sortOrderValues[currentSortOrderIndex])
|
startFileViewer(ImageViewer::class.java, fullPath)
|
||||||
}
|
}
|
||||||
isVideo(fullPath) -> {
|
isVideo(fullPath) -> {
|
||||||
startFileViewer(VideoPlayer::class.java, fullPath)
|
startFileViewer(VideoPlayer::class.java, fullPath)
|
||||||
|
@ -9,6 +9,22 @@ class AudioPlayer: MediaPlayer(){
|
|||||||
override fun viewFile() {
|
override fun viewFile() {
|
||||||
setContentView(R.layout.activity_audio_player)
|
setContentView(R.layout.activity_audio_player)
|
||||||
super.viewFile()
|
super.viewFile()
|
||||||
|
refreshFileName()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getFileType(): String {
|
||||||
|
return "audio"
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun bindPlayer(player: SimpleExoPlayer) {
|
||||||
|
audio_controller.player = player
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onPlaylistIndexChanged() {
|
||||||
|
refreshFileName()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun refreshFileName() {
|
||||||
val filename = File(filePath).name
|
val filename = File(filePath).name
|
||||||
val pos = filename.lastIndexOf('.')
|
val pos = filename.lastIndexOf('.')
|
||||||
music_title.text = if (pos != -1){
|
music_title.text = if (pos != -1){
|
||||||
@ -17,8 +33,4 @@ class AudioPlayer: MediaPlayer(){
|
|||||||
filename
|
filename
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun bindPlayer(player: SimpleExoPlayer) {
|
|
||||||
audio_controller.player = player
|
|
||||||
}
|
|
||||||
}
|
}
|
@ -3,16 +3,23 @@ package sushi.hardcore.droidfs.file_viewers
|
|||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import sushi.hardcore.droidfs.BaseActivity
|
import sushi.hardcore.droidfs.BaseActivity
|
||||||
|
import sushi.hardcore.droidfs.ConstValues
|
||||||
|
import sushi.hardcore.droidfs.GocryptfsVolume
|
||||||
import sushi.hardcore.droidfs.R
|
import sushi.hardcore.droidfs.R
|
||||||
import sushi.hardcore.droidfs.content_providers.RestrictedFileProvider
|
import sushi.hardcore.droidfs.content_providers.RestrictedFileProvider
|
||||||
import sushi.hardcore.droidfs.GocryptfsVolume
|
import sushi.hardcore.droidfs.explorers.ExplorerElement
|
||||||
|
import sushi.hardcore.droidfs.util.PathUtils
|
||||||
import sushi.hardcore.droidfs.widgets.ColoredAlertDialogBuilder
|
import sushi.hardcore.droidfs.widgets.ColoredAlertDialogBuilder
|
||||||
|
|
||||||
abstract class FileViewerActivity: BaseActivity() {
|
abstract class FileViewerActivity: BaseActivity() {
|
||||||
lateinit var gocryptfsVolume: GocryptfsVolume
|
protected lateinit var gocryptfsVolume: GocryptfsVolume
|
||||||
lateinit var filePath: String
|
protected lateinit var filePath: String
|
||||||
private var isFinishingIntentionally = false
|
private var isFinishingIntentionally = false
|
||||||
private var usf_keep_open = false
|
private var usf_keep_open = false
|
||||||
|
private var wasMapped = false
|
||||||
|
protected val mappedPlaylist = mutableListOf<ExplorerElement>()
|
||||||
|
protected var currentPlaylistIndex = -1
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
filePath = intent.getStringExtra("path")!!
|
filePath = intent.getStringExtra("path")!!
|
||||||
@ -22,6 +29,7 @@ abstract class FileViewerActivity: BaseActivity() {
|
|||||||
hideSystemUi()
|
hideSystemUi()
|
||||||
viewFile()
|
viewFile()
|
||||||
}
|
}
|
||||||
|
|
||||||
open fun hideSystemUi(){
|
open fun hideSystemUi(){
|
||||||
window.decorView.systemUiVisibility =
|
window.decorView.systemUiVisibility =
|
||||||
View.SYSTEM_UI_FLAG_FULLSCREEN/* or
|
View.SYSTEM_UI_FLAG_FULLSCREEN/* or
|
||||||
@ -31,14 +39,18 @@ abstract class FileViewerActivity: BaseActivity() {
|
|||||||
View.SYSTEM_UI_FLAG_HIDE_NAVIGATION or
|
View.SYSTEM_UI_FLAG_HIDE_NAVIGATION or
|
||||||
View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION*/
|
View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION*/
|
||||||
}
|
}
|
||||||
|
|
||||||
|
abstract fun getFileType(): String
|
||||||
abstract fun viewFile()
|
abstract fun viewFile()
|
||||||
|
|
||||||
override fun onUserInteraction() {
|
override fun onUserInteraction() {
|
||||||
super.onUserInteraction()
|
super.onUserInteraction()
|
||||||
if (window.decorView.systemUiVisibility and View.SYSTEM_UI_FLAG_FULLSCREEN == 0){
|
if (window.decorView.systemUiVisibility and View.SYSTEM_UI_FLAG_FULLSCREEN == 0){
|
||||||
hideSystemUi()
|
hideSystemUi()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fun loadWholeFile(path: String): ByteArray? {
|
|
||||||
|
protected fun loadWholeFile(path: String): ByteArray? {
|
||||||
val fileSize = gocryptfsVolume.getSize(path)
|
val fileSize = gocryptfsVolume.getSize(path)
|
||||||
if (fileSize >= 0){
|
if (fileSize >= 0){
|
||||||
try {
|
try {
|
||||||
@ -86,7 +98,49 @@ abstract class FileViewerActivity: BaseActivity() {
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
protected fun goBackToExplorer(){
|
protected fun createPlaylist() {
|
||||||
|
if (!wasMapped){
|
||||||
|
for (e in gocryptfsVolume.recursiveMapFiles(PathUtils.getParentPath(filePath))){
|
||||||
|
if (e.isRegularFile) {
|
||||||
|
if (ConstValues.isExtensionType(getFileType(), e.name) || filePath == e.fullPath) {
|
||||||
|
mappedPlaylist.add(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val sortOrder = intent.getStringExtra("sortOrder") ?: "name"
|
||||||
|
ExplorerElement.sortBy(sortOrder, mappedPlaylist)
|
||||||
|
//find current index
|
||||||
|
for ((i, e) in mappedPlaylist.withIndex()){
|
||||||
|
if (filePath == e.fullPath){
|
||||||
|
currentPlaylistIndex = i
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
wasMapped = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected fun playlistNext(forward: Boolean) {
|
||||||
|
createPlaylist()
|
||||||
|
currentPlaylistIndex = if (forward) {
|
||||||
|
(currentPlaylistIndex+1)%mappedPlaylist.size
|
||||||
|
} else {
|
||||||
|
var x = (currentPlaylistIndex-1)%mappedPlaylist.size
|
||||||
|
if (x < 0) {
|
||||||
|
x += mappedPlaylist.size
|
||||||
|
}
|
||||||
|
x
|
||||||
|
}
|
||||||
|
filePath = mappedPlaylist[currentPlaylistIndex].fullPath
|
||||||
|
}
|
||||||
|
|
||||||
|
protected fun refreshPlaylist() {
|
||||||
|
mappedPlaylist.clear()
|
||||||
|
wasMapped = false
|
||||||
|
createPlaylist()
|
||||||
|
}
|
||||||
|
|
||||||
|
protected fun goBackToExplorer() {
|
||||||
isFinishingIntentionally = true
|
isFinishingIntentionally = true
|
||||||
finish()
|
finish()
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
package sushi.hardcore.droidfs.file_viewers
|
package sushi.hardcore.droidfs.file_viewers
|
||||||
|
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import com.google.android.exoplayer2.upstream.*
|
import com.google.android.exoplayer2.upstream.DataSource
|
||||||
|
import com.google.android.exoplayer2.upstream.DataSpec
|
||||||
|
import com.google.android.exoplayer2.upstream.TransferListener
|
||||||
import sushi.hardcore.droidfs.ConstValues
|
import sushi.hardcore.droidfs.ConstValues
|
||||||
import sushi.hardcore.droidfs.GocryptfsVolume
|
import sushi.hardcore.droidfs.GocryptfsVolume
|
||||||
import kotlin.math.ceil
|
import kotlin.math.ceil
|
||||||
@ -11,10 +13,8 @@ class GocryptfsDataSource(private val gocryptfsVolume: GocryptfsVolume, private
|
|||||||
private var handleID = -1
|
private var handleID = -1
|
||||||
private var fileSize: Long = -1
|
private var fileSize: Long = -1
|
||||||
private var fileOffset: Long = 0
|
private var fileOffset: Long = 0
|
||||||
override fun open(dataSpec: DataSpec?): Long {
|
override fun open(dataSpec: DataSpec): Long {
|
||||||
dataSpec?.let {
|
fileOffset = dataSpec.position
|
||||||
fileOffset = dataSpec.position
|
|
||||||
}
|
|
||||||
handleID = gocryptfsVolume.openReadMode(filePath)
|
handleID = gocryptfsVolume.openReadMode(filePath)
|
||||||
fileSize = gocryptfsVolume.getSize(filePath)
|
fileSize = gocryptfsVolume.getSize(filePath)
|
||||||
return fileSize
|
return fileSize
|
||||||
@ -28,7 +28,7 @@ class GocryptfsDataSource(private val gocryptfsVolume: GocryptfsVolume, private
|
|||||||
gocryptfsVolume.closeFile(handleID)
|
gocryptfsVolume.closeFile(handleID)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun addTransferListener(transferListener: TransferListener?) {
|
override fun addTransferListener(transferListener: TransferListener) {
|
||||||
//too lazy to implement this
|
//too lazy to implement this
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -44,7 +44,7 @@ class GocryptfsDataSource(private val gocryptfsVolume: GocryptfsVolume, private
|
|||||||
} else {
|
} else {
|
||||||
ByteArray(tmpReadLength)
|
ByteArray(tmpReadLength)
|
||||||
}
|
}
|
||||||
val read = gocryptfsVolume.readFile(handleID, fileOffset, tmpBuff)
|
val read = gocryptfsVolume.readFile(handleID, fileOffset, tmpBuff)
|
||||||
System.arraycopy(tmpBuff, 0, buffer, offset+totalRead, read)
|
System.arraycopy(tmpBuff, 0, buffer, offset+totalRead, read)
|
||||||
fileOffset += read
|
fileOffset += read
|
||||||
totalRead += read
|
totalRead += read
|
||||||
@ -52,7 +52,7 @@ class GocryptfsDataSource(private val gocryptfsVolume: GocryptfsVolume, private
|
|||||||
return totalRead
|
return totalRead
|
||||||
}
|
}
|
||||||
|
|
||||||
class Factory(private val gocryptfsVolume: GocryptfsVolume, private val filePath: String): DataSource.Factory{
|
class Factory(private val gocryptfsVolume: GocryptfsVolume, private val filePath: String): DataSource.Factory {
|
||||||
override fun createDataSource(): DataSource {
|
override fun createDataSource(): DataSource {
|
||||||
return GocryptfsDataSource(gocryptfsVolume, filePath)
|
return GocryptfsDataSource(gocryptfsVolume, filePath)
|
||||||
}
|
}
|
||||||
|
@ -16,9 +16,6 @@ import com.bumptech.glide.load.resource.bitmap.BitmapTransformation
|
|||||||
import kotlinx.android.synthetic.main.activity_image_viewer.*
|
import kotlinx.android.synthetic.main.activity_image_viewer.*
|
||||||
import sushi.hardcore.droidfs.ConstValues
|
import sushi.hardcore.droidfs.ConstValues
|
||||||
import sushi.hardcore.droidfs.R
|
import sushi.hardcore.droidfs.R
|
||||||
import sushi.hardcore.droidfs.explorers.ExplorerElement
|
|
||||||
import sushi.hardcore.droidfs.util.MiscUtils
|
|
||||||
import sushi.hardcore.droidfs.util.PathUtils
|
|
||||||
import sushi.hardcore.droidfs.widgets.ColoredAlertDialogBuilder
|
import sushi.hardcore.droidfs.widgets.ColoredAlertDialogBuilder
|
||||||
import sushi.hardcore.droidfs.widgets.ZoomableImageView
|
import sushi.hardcore.droidfs.widgets.ZoomableImageView
|
||||||
import java.io.ByteArrayInputStream
|
import java.io.ByteArrayInputStream
|
||||||
@ -36,11 +33,7 @@ class ImageViewer: FileViewerActivity() {
|
|||||||
private lateinit var glideImage: RequestBuilder<Drawable>
|
private lateinit var glideImage: RequestBuilder<Drawable>
|
||||||
private var x1 = 0F
|
private var x1 = 0F
|
||||||
private var x2 = 0F
|
private var x2 = 0F
|
||||||
private val mappedImages = mutableListOf<ExplorerElement>()
|
|
||||||
private lateinit var sortOrder: String
|
|
||||||
private var wasMapped = false
|
|
||||||
private var slideshowActive = false
|
private var slideshowActive = false
|
||||||
private var currentMappedImageIndex = -1
|
|
||||||
private var rotationAngle: Float = 0F
|
private var rotationAngle: Float = 0F
|
||||||
private var rotatedBitmap: Bitmap? = null
|
private var rotatedBitmap: Bitmap? = null
|
||||||
private val handler = Handler()
|
private val handler = Handler()
|
||||||
@ -54,6 +47,11 @@ class ImageViewer: FileViewerActivity() {
|
|||||||
swipeImage(-1F, true)
|
swipeImage(-1F, true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun getFileType(): String {
|
||||||
|
return "image"
|
||||||
|
}
|
||||||
|
|
||||||
override fun viewFile() {
|
override fun viewFile() {
|
||||||
setContentView(R.layout.activity_image_viewer)
|
setContentView(R.layout.activity_image_viewer)
|
||||||
image_viewer.setOnInteractionListener(object : ZoomableImageView.OnInteractionListener {
|
image_viewer.setOnInteractionListener(object : ZoomableImageView.OnInteractionListener {
|
||||||
@ -106,38 +104,13 @@ class ImageViewer: FileViewerActivity() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun swipeImage(deltaX: Float, slideshowSwipe: Boolean = false){
|
private fun swipeImage(deltaX: Float, slideshowSwipe: Boolean = false){
|
||||||
if (!wasMapped){
|
playlistNext(deltaX < 0)
|
||||||
for (e in gocryptfsVolume.recursiveMapFiles(PathUtils.getParentPath(filePath))){
|
loadImage()
|
||||||
if (e.isRegularFile && ConstValues.isImage(e.name)){
|
if (slideshowActive){
|
||||||
mappedImages.add(e)
|
if (!slideshowSwipe) { //reset slideshow delay if user swipes
|
||||||
}
|
handler.removeCallbacks(slideshowNext)
|
||||||
}
|
|
||||||
sortOrder = intent.getStringExtra("sortOrder") ?: "name"
|
|
||||||
ExplorerElement.sortBy(sortOrder, mappedImages)
|
|
||||||
for ((i, e) in mappedImages.withIndex()){
|
|
||||||
if (filePath == e.fullPath){
|
|
||||||
currentMappedImageIndex = i
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
wasMapped = true
|
|
||||||
}
|
|
||||||
if (mappedImages.size == 0){ //can happen on deleting images
|
|
||||||
goBackToExplorer()
|
|
||||||
} else {
|
|
||||||
currentMappedImageIndex = if (deltaX < 0){
|
|
||||||
MiscUtils.incrementIndex(currentMappedImageIndex, mappedImages)
|
|
||||||
} else {
|
|
||||||
MiscUtils.decrementIndex(currentMappedImageIndex, mappedImages)
|
|
||||||
}
|
|
||||||
filePath = mappedImages[currentMappedImageIndex].fullPath
|
|
||||||
loadImage()
|
|
||||||
if (slideshowActive){
|
|
||||||
if (!slideshowSwipe) { //reset slideshow delay if user swipes
|
|
||||||
handler.removeCallbacks(slideshowNext)
|
|
||||||
}
|
|
||||||
handler.postDelayed(slideshowNext, ConstValues.slideshow_delay)
|
|
||||||
}
|
}
|
||||||
|
handler.postDelayed(slideshowNext, ConstValues.slideshow_delay)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -147,10 +120,13 @@ class ImageViewer: FileViewerActivity() {
|
|||||||
.setTitle(R.string.warning)
|
.setTitle(R.string.warning)
|
||||||
.setPositiveButton(R.string.ok) { _, _ ->
|
.setPositiveButton(R.string.ok) { _, _ ->
|
||||||
if (gocryptfsVolume.removeFile(filePath)){
|
if (gocryptfsVolume.removeFile(filePath)){
|
||||||
currentMappedImageIndex = MiscUtils.decrementIndex(currentMappedImageIndex, mappedImages)
|
playlistNext(true)
|
||||||
mappedImages.clear()
|
refreshPlaylist()
|
||||||
wasMapped = false
|
if (mappedPlaylist.size == 0) { //deleted all images of the playlist
|
||||||
swipeImage(-1F)
|
goBackToExplorer()
|
||||||
|
} else {
|
||||||
|
loadImage()
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
ColoredAlertDialogBuilder(this)
|
ColoredAlertDialogBuilder(this)
|
||||||
.keepFullScreen()
|
.keepFullScreen()
|
||||||
|
@ -1,18 +1,17 @@
|
|||||||
package sushi.hardcore.droidfs.file_viewers
|
package sushi.hardcore.droidfs.file_viewers
|
||||||
|
|
||||||
import android.view.WindowManager
|
import android.view.WindowManager
|
||||||
import androidx.appcompat.app.AlertDialog
|
|
||||||
import com.google.android.exoplayer2.ExoPlaybackException
|
import com.google.android.exoplayer2.ExoPlaybackException
|
||||||
|
import com.google.android.exoplayer2.MediaItem
|
||||||
import com.google.android.exoplayer2.Player
|
import com.google.android.exoplayer2.Player
|
||||||
import com.google.android.exoplayer2.SimpleExoPlayer
|
import com.google.android.exoplayer2.SimpleExoPlayer
|
||||||
import com.google.android.exoplayer2.extractor.ExtractorsFactory
|
|
||||||
import com.google.android.exoplayer2.extractor.flac.FlacExtractor
|
import com.google.android.exoplayer2.extractor.flac.FlacExtractor
|
||||||
import com.google.android.exoplayer2.extractor.mkv.MatroskaExtractor
|
import com.google.android.exoplayer2.extractor.mkv.MatroskaExtractor
|
||||||
import com.google.android.exoplayer2.extractor.mp3.Mp3Extractor
|
import com.google.android.exoplayer2.extractor.mp3.Mp3Extractor
|
||||||
import com.google.android.exoplayer2.extractor.mp4.Mp4Extractor
|
import com.google.android.exoplayer2.extractor.mp4.Mp4Extractor
|
||||||
import com.google.android.exoplayer2.extractor.ogg.OggExtractor
|
import com.google.android.exoplayer2.extractor.ogg.OggExtractor
|
||||||
import com.google.android.exoplayer2.extractor.wav.WavExtractor
|
import com.google.android.exoplayer2.extractor.wav.WavExtractor
|
||||||
import com.google.android.exoplayer2.source.LoopingMediaSource
|
import com.google.android.exoplayer2.source.MediaSource
|
||||||
import com.google.android.exoplayer2.source.ProgressiveMediaSource
|
import com.google.android.exoplayer2.source.ProgressiveMediaSource
|
||||||
import sushi.hardcore.droidfs.ConstValues
|
import sushi.hardcore.droidfs.ConstValues
|
||||||
import sushi.hardcore.droidfs.R
|
import sushi.hardcore.droidfs.R
|
||||||
@ -20,46 +19,48 @@ import sushi.hardcore.droidfs.widgets.ColoredAlertDialogBuilder
|
|||||||
|
|
||||||
abstract class MediaPlayer: FileViewerActivity() {
|
abstract class MediaPlayer: FileViewerActivity() {
|
||||||
private lateinit var player: SimpleExoPlayer
|
private lateinit var player: SimpleExoPlayer
|
||||||
private var currentWindow = 0
|
|
||||||
private var playbackPosition: Long = 0
|
|
||||||
private lateinit var errorDialog: AlertDialog
|
|
||||||
|
|
||||||
override fun viewFile() {
|
override fun viewFile() {
|
||||||
errorDialog = ColoredAlertDialogBuilder(this)
|
|
||||||
.setTitle(R.string.error)
|
|
||||||
.setMessage(R.string.playing_failed)
|
|
||||||
.setCancelable(false)
|
|
||||||
.setPositiveButton(R.string.ok) { _, _ -> goBackToExplorer()}
|
|
||||||
.create()
|
|
||||||
hideSystemUi()
|
hideSystemUi()
|
||||||
initializePlayer()
|
initializePlayer()
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract fun bindPlayer(player: SimpleExoPlayer)
|
abstract fun bindPlayer(player: SimpleExoPlayer)
|
||||||
|
protected open fun onPlaylistIndexChanged() {}
|
||||||
|
|
||||||
private fun initializePlayer(){
|
private fun createMediaSource(filePath: String): MediaSource {
|
||||||
player = SimpleExoPlayer.Builder(this).build()
|
|
||||||
bindPlayer(player)
|
|
||||||
val dataSourceFactory = GocryptfsDataSource.Factory(gocryptfsVolume, filePath)
|
val dataSourceFactory = GocryptfsDataSource.Factory(gocryptfsVolume, filePath)
|
||||||
val mediaSource = ProgressiveMediaSource.Factory(dataSourceFactory, ExtractorsFactory {
|
return ProgressiveMediaSource.Factory(dataSourceFactory, { arrayOf(
|
||||||
arrayOf(
|
|
||||||
MatroskaExtractor(),
|
MatroskaExtractor(),
|
||||||
Mp4Extractor(),
|
Mp4Extractor(),
|
||||||
Mp3Extractor(),
|
Mp3Extractor(),
|
||||||
OggExtractor(),
|
OggExtractor(),
|
||||||
WavExtractor(),
|
WavExtractor(),
|
||||||
FlacExtractor()
|
FlacExtractor()
|
||||||
)
|
) }).createMediaSource(MediaItem.fromUri(ConstValues.fakeUri))
|
||||||
}).createMediaSource(ConstValues.fakeUri)
|
}
|
||||||
player.seekTo(currentWindow, playbackPosition)
|
|
||||||
|
private fun initializePlayer(){
|
||||||
|
player = SimpleExoPlayer.Builder(this).build()
|
||||||
|
bindPlayer(player)
|
||||||
|
createPlaylist()
|
||||||
|
for (e in mappedPlaylist) {
|
||||||
|
player.addMediaSource(createMediaSource(e.fullPath))
|
||||||
|
}
|
||||||
|
player.repeatMode = Player.REPEAT_MODE_ALL
|
||||||
|
player.seekToDefaultPosition(currentPlaylistIndex)
|
||||||
player.playWhenReady = true
|
player.playWhenReady = true
|
||||||
player.addListener(object : Player.EventListener{
|
player.addListener(object : Player.EventListener{
|
||||||
override fun onPlayerError(error: ExoPlaybackException) {
|
override fun onPlayerError(error: ExoPlaybackException) {
|
||||||
if (error.type == ExoPlaybackException.TYPE_SOURCE){
|
if (error.type == ExoPlaybackException.TYPE_SOURCE){
|
||||||
errorDialog.show()
|
ColoredAlertDialogBuilder(this@MediaPlayer)
|
||||||
|
.setTitle(R.string.error)
|
||||||
|
.setMessage(R.string.playing_failed)
|
||||||
|
.setCancelable(false)
|
||||||
|
.setPositiveButton(R.string.ok) { _, _ -> goBackToExplorer()}
|
||||||
|
.show()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onIsPlayingChanged(isPlaying: Boolean) {
|
override fun onIsPlayingChanged(isPlaying: Boolean) {
|
||||||
if (isPlaying){
|
if (isPlaying){
|
||||||
window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
|
window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
|
||||||
@ -67,20 +68,20 @@ abstract class MediaPlayer: FileViewerActivity() {
|
|||||||
window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
|
window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
override fun onPositionDiscontinuity(reason: Int) {
|
||||||
|
if (player.currentWindowIndex != currentPlaylistIndex) {
|
||||||
|
playlistNext(player.currentWindowIndex == (currentPlaylistIndex+1)%mappedPlaylist.size)
|
||||||
|
onPlaylistIndexChanged()
|
||||||
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
player.prepare(LoopingMediaSource(mediaSource), false, false)
|
player.prepare()
|
||||||
}
|
|
||||||
|
|
||||||
private fun releasePlayer(){
|
|
||||||
if (::player.isInitialized) {
|
|
||||||
playbackPosition = player.currentPosition
|
|
||||||
currentWindow = player.currentWindowIndex
|
|
||||||
player.release()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDestroy() {
|
override fun onDestroy() {
|
||||||
super.onDestroy()
|
super.onDestroy()
|
||||||
releasePlayer()
|
if (::player.isInitialized) {
|
||||||
|
player.release()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,5 +1,6 @@
|
|||||||
package sushi.hardcore.droidfs.file_viewers
|
package sushi.hardcore.droidfs.file_viewers
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
import android.text.Editable
|
import android.text.Editable
|
||||||
import android.text.TextWatcher
|
import android.text.TextWatcher
|
||||||
import android.view.Menu
|
import android.view.Menu
|
||||||
@ -24,6 +25,11 @@ class TextEditor: FileViewerActivity() {
|
|||||||
override fun hideSystemUi() {
|
override fun hideSystemUi() {
|
||||||
//don't hide system ui
|
//don't hide system ui
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun getFileType(): String {
|
||||||
|
return "text"
|
||||||
|
}
|
||||||
|
|
||||||
override fun viewFile() {
|
override fun viewFile() {
|
||||||
loadWholeFile(filePath)?.let {
|
loadWholeFile(filePath)?.let {
|
||||||
fileName = File(filePath).name
|
fileName = File(filePath).name
|
||||||
@ -60,6 +66,7 @@ class TextEditor: FileViewerActivity() {
|
|||||||
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
|
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
|
||||||
if (!changedSinceLastSave){
|
if (!changedSinceLastSave){
|
||||||
changedSinceLastSave = true
|
changedSinceLastSave = true
|
||||||
|
@SuppressLint("SetTextI18n")
|
||||||
titleText.text = "*$fileName"
|
titleText.text = "*$fileName"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,4 +13,8 @@ class VideoPlayer: MediaPlayer() {
|
|||||||
override fun bindPlayer(player: SimpleExoPlayer) {
|
override fun bindPlayer(player: SimpleExoPlayer) {
|
||||||
video_player.player = player
|
video_player.player = player
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun getFileType(): String {
|
||||||
|
return "video"
|
||||||
|
}
|
||||||
}
|
}
|
@ -1,18 +0,0 @@
|
|||||||
package sushi.hardcore.droidfs.util
|
|
||||||
|
|
||||||
object MiscUtils {
|
|
||||||
fun incrementIndex(index: Int, list: List<Any>): Int {
|
|
||||||
var i = index+1
|
|
||||||
if (i >= list.size){
|
|
||||||
i = 0
|
|
||||||
}
|
|
||||||
return i
|
|
||||||
}
|
|
||||||
fun decrementIndex(index: Int, list: List<Any>): Int {
|
|
||||||
var i = index-1
|
|
||||||
if (i < 0){
|
|
||||||
i = list.size-1
|
|
||||||
}
|
|
||||||
return i
|
|
||||||
}
|
|
||||||
}
|
|
@ -19,7 +19,6 @@
|
|||||||
android:id="@+id/audio_controller"
|
android:id="@+id/audio_controller"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
app:controller_layout_id="@layout/exo_custom_playback_control"
|
|
||||||
app:show_timeout="0"/>
|
app:show_timeout="0"/>
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
@ -2,15 +2,13 @@
|
|||||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
|
||||||
android:gravity="center"
|
android:gravity="center"
|
||||||
android:background="@color/fullScreenBackgroundColor">
|
android:background="@color/fullScreenBackgroundColor">
|
||||||
|
|
||||||
<com.google.android.exoplayer2.ui.SimpleExoPlayerView
|
<com.google.android.exoplayer2.ui.PlayerView
|
||||||
android:id="@+id/video_player"
|
android:id="@+id/video_player"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:layout_gravity="center"
|
android:layout_gravity="center"/>
|
||||||
app:controller_layout_id="@layout/exo_custom_playback_control"/>
|
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
@ -1,74 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_gravity="bottom"
|
|
||||||
android:layoutDirection="ltr"
|
|
||||||
android:background="#CC000000"
|
|
||||||
android:orientation="vertical">
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:gravity="center"
|
|
||||||
android:paddingTop="4dp"
|
|
||||||
android:orientation="horizontal">
|
|
||||||
|
|
||||||
<ImageButton android:id="@id/exo_rew"
|
|
||||||
style="@style/ExoMediaButton.Rewind"/>
|
|
||||||
|
|
||||||
<ImageButton android:id="@id/exo_shuffle"
|
|
||||||
style="@style/ExoMediaButton"/>
|
|
||||||
|
|
||||||
<ImageButton android:id="@id/exo_repeat_toggle"
|
|
||||||
style="@style/ExoMediaButton"/>
|
|
||||||
|
|
||||||
<ImageButton android:id="@id/exo_play"
|
|
||||||
style="@style/ExoMediaButton.Play"/>
|
|
||||||
|
|
||||||
<ImageButton android:id="@id/exo_pause"
|
|
||||||
style="@style/ExoMediaButton.Pause"/>
|
|
||||||
|
|
||||||
<ImageButton android:id="@id/exo_ffwd"
|
|
||||||
style="@style/ExoMediaButton.FastForward"/>
|
|
||||||
|
|
||||||
<ImageButton android:id="@id/exo_vr"
|
|
||||||
style="@style/ExoMediaButton.VR"/>
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="4dp"
|
|
||||||
android:gravity="center_vertical"
|
|
||||||
android:orientation="horizontal">
|
|
||||||
|
|
||||||
<TextView android:id="@id/exo_position"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:textSize="14sp"
|
|
||||||
android:textStyle="bold"
|
|
||||||
android:paddingLeft="4dp"
|
|
||||||
android:paddingRight="4dp"
|
|
||||||
android:includeFontPadding="false"
|
|
||||||
android:textColor="#FFBEBEBE"/>
|
|
||||||
|
|
||||||
<View android:id="@id/exo_progress_placeholder"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_weight="1"
|
|
||||||
android:layout_height="26dp"/>
|
|
||||||
|
|
||||||
<TextView android:id="@id/exo_duration"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:textSize="14sp"
|
|
||||||
android:textStyle="bold"
|
|
||||||
android:paddingLeft="4dp"
|
|
||||||
android:paddingRight="4dp"
|
|
||||||
android:includeFontPadding="false"
|
|
||||||
android:textColor="#FFBEBEBE"/>
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
Loading…
Reference in New Issue
Block a user