Skip to content
Snippets Groups Projects
Commit e9f587d5 authored by Nicolas Pomepuy's avatar Nicolas Pomepuy Committed by Geoffrey Métais
Browse files

Refactor BrowserModel: add a delegate to perform the path operations

parent 338a6c82
No related branches found
No related tags found
1 merge request!242Breadcrumb backstack
......@@ -6,7 +6,6 @@ import android.graphics.drawable.BitmapDrawable
import android.net.Uri
import android.os.Bundle
import android.os.Parcelable
import android.util.Log
import android.view.Gravity
import android.view.View
import androidx.coordinatorlayout.widget.CoordinatorLayout
......@@ -27,7 +26,6 @@ import org.videolan.medialibrary.interfaces.AbstractMedialibrary
import org.videolan.medialibrary.interfaces.media.AbstractArtist
import org.videolan.medialibrary.interfaces.media.AbstractMediaWrapper
import org.videolan.medialibrary.media.MediaLibraryItem
import org.videolan.vlc.BuildConfig
import org.videolan.vlc.R
import org.videolan.vlc.databinding.InfoActivityBinding
import org.videolan.vlc.gui.browser.PathAdapter
......@@ -39,6 +37,8 @@ import org.videolan.vlc.gui.video.MediaInfoAdapter
import org.videolan.vlc.gui.view.VLCDividerItemDecoration
import org.videolan.vlc.media.MediaUtils
import org.videolan.vlc.util.*
import org.videolan.vlc.viewmodels.browser.IPathOperationDelegate
import org.videolan.vlc.viewmodels.browser.PathOperationDelegate
import java.io.File
import java.util.*
......@@ -47,7 +47,7 @@ private const val TAG_FAB_VISIBILITY = "FAB"
@ObsoleteCoroutinesApi
@ExperimentalCoroutinesApi
class InfoActivity : AudioPlayerContainerActivity(), View.OnClickListener, PathAdapterListener {
class InfoActivity : AudioPlayerContainerActivity(), View.OnClickListener, PathAdapterListener, IPathOperationDelegate by PathOperationDelegate() {
private lateinit var item: MediaLibraryItem
private lateinit var adapter: MediaInfoAdapter
......@@ -179,6 +179,8 @@ class InfoActivity : AudioPlayerContainerActivity(), View.OnClickListener, PathA
override fun showRoot() = false
override fun getPathOperationDelegate() = this
override fun onPostCreate(savedInstanceState: Bundle?) {
fragmentContainer = binding.container
super.onPostCreate(savedInstanceState)
......
......@@ -64,6 +64,7 @@ import org.videolan.vlc.media.PlaylistManager
import org.videolan.vlc.repository.BrowserFavRepository
import org.videolan.vlc.util.*
import org.videolan.vlc.viewmodels.browser.BrowserModel
import org.videolan.vlc.viewmodels.browser.IPathOperationDelegate
import java.util.*
private const val TAG = "VLC/BaseBrowserFragment"
......@@ -174,6 +175,8 @@ abstract class BaseBrowserFragment : MediaBrowserFragment<BrowserModel>(), IRefr
override fun showRoot() = true
override fun getPathOperationDelegate() = viewModel
override fun onStart() {
super.onStart()
fabPlay?.run {
......
......@@ -2,28 +2,28 @@ package org.videolan.vlc.gui.browser
import android.content.Context
import android.net.Uri
import android.util.Base64
import android.util.Log
import android.view.LayoutInflater
import android.view.ViewGroup
import android.widget.TextView
import androidx.collection.SimpleArrayMap
import androidx.recyclerview.widget.RecyclerView
import org.videolan.medialibrary.interfaces.media.AbstractMediaWrapper
import org.videolan.medialibrary.media.MediaLibraryItem
import org.videolan.vlc.BuildConfig
import org.videolan.vlc.R
import org.videolan.vlc.util.AndroidDevices
private val storages = SimpleArrayMap<String, String>()
import org.videolan.vlc.viewmodels.browser.IPathOperationDelegate
import org.videolan.vlc.viewmodels.browser.PathOperationDelegate
class PathAdapter(val browser: PathAdapterListener, media: AbstractMediaWrapper) : RecyclerView.Adapter<PathAdapter.ViewHolder>() {
private val pathOperationDelegate = browser.getPathOperationDelegate()
init {
//we save Base64 encoded strings to be used in substitutions to avoid false positives if a user directory is named as the media title
// ie "SDCARD", "Internal Memory" and so on
if (media.hasStateFlags(MediaLibraryItem.FLAG_STORAGE)) storages.put(Uri.decode(media.uri.path), makePathSafe(media.title))
storages.put(AndroidDevices.EXTERNAL_PUBLIC_DIRECTORY, makePathSafe(browser.currentContext().getString(R.string.internal_memory)))
if (media.hasStateFlags(MediaLibraryItem.FLAG_STORAGE)) PathOperationDelegate.storages.put(Uri.decode(media.uri.path), pathOperationDelegate.makePathSafe(media.title))
PathOperationDelegate.storages.put(AndroidDevices.EXTERNAL_PUBLIC_DIRECTORY, pathOperationDelegate.makePathSafe(browser.currentContext().getString(R.string.internal_memory)))
}
private val browserTitle = browser.currentContext().getString(R.string.browser)
......@@ -39,7 +39,7 @@ class PathAdapter(val browser: PathAdapterListener, media: AbstractMediaWrapper)
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.root.text = when {
//substitute a storage path to its name. See [replaceStoragePath]
storages.containsKey(Uri.parse(segments[position]).path) -> retrieveSafePath(storages.valueAt(storages.indexOfKey(Uri.parse(segments[position]).path)))
PathOperationDelegate.storages.containsKey(Uri.parse(segments[position]).path) -> pathOperationDelegate.retrieveSafePath(PathOperationDelegate.storages.valueAt(PathOperationDelegate.storages.indexOfKey(Uri.parse(segments[position]).path)))
else -> Uri.parse(segments[position]).lastPathSegment
}
}
......@@ -56,7 +56,6 @@ class PathAdapter(val browser: PathAdapterListener, media: AbstractMediaWrapper)
}
}
}
/**
* Splits an [Uri] in a list of string used as the adapter items
* Each item is a string representing a valid path
......@@ -69,7 +68,7 @@ class PathAdapter(val browser: PathAdapterListener, media: AbstractMediaWrapper)
val isOtg = path.startsWith("/tree/")
val string = when {
isOtg -> if (path.endsWith(':')) "" else path.substringAfterLast(':')
else -> replaceStoragePath(path)
else -> pathOperationDelegate.replaceStoragePath(path)
}
val list: MutableList<String> = mutableListOf()
if (isOtg) list.add(otgDevice)
......@@ -80,7 +79,7 @@ class PathAdapter(val browser: PathAdapterListener, media: AbstractMediaWrapper)
//start creating the Uri
val currentPathUri = Uri.Builder().scheme(uri.scheme).authority(uri.authority)
//append all the previous paths and the current one
for (i in 0..index) appendPathToUri(pathParts[i], currentPathUri)
for (i in 0..index) pathOperationDelegate.appendPathToUri(pathParts[i], currentPathUri)
list.add(currentPathUri.toString())
}
if (BuildConfig.DEBUG) list.forEach { Log.d(this::class.java.simpleName, "Added in breadcrumb: $it") }
......@@ -89,55 +88,13 @@ class PathAdapter(val browser: PathAdapterListener, media: AbstractMediaWrapper)
return list
}
/**
* Append a path to the Uri from a String
* It takes care of substituting paths stored in [storages] and splitting if the substituted path contains file separators
*
* @param path the path to append
* @param uri the uri the path should be appended to
*
*/
private fun appendPathToUri(path: String, uri: Uri.Builder) {
var newPath = path
for (i in 0..storages.size()) if (storages.valueAt(i) == newPath) newPath = storages.keyAt(i)
newPath.split('/').forEach {
uri.appendPath(it)
}
}
/**
* Substitutes the [storages]keys by the [storages] values
*
* @param path the real path string
* @return the path string with substitutions
*/
private fun replaceStoragePath(path: String): String {
try {
if (storages.size() > 0) for (i in 0..storages.size()) if (path.startsWith(storages.keyAt(i))) return path.replace(storages.keyAt(i), storages.valueAt(i))
} catch (e: IllegalStateException) {
}
return path
}
/**
* Encodes a String to avoid false positive substitusions
*
* @param path the path to encode
* @return the encoded path
*/
private fun makePathSafe(path: String) = Base64.encodeToString(path.toByteArray(), Base64.DEFAULT)
/**
* Decodes a string previously encoded with [makePathSafe]
*
* @param encoded the encoded path string
* @return the decoded path string
*/
private fun retrieveSafePath(encoded: String) = String(Base64.decode(encoded, Base64.DEFAULT))
}
interface PathAdapterListener {
fun backTo(tag: String)
fun currentContext(): Context
fun showRoot(): Boolean
fun getPathOperationDelegate(): IPathOperationDelegate
}
\ No newline at end of file
......@@ -34,10 +34,7 @@ import org.videolan.vlc.util.CATEGORY
import org.videolan.vlc.util.FileUtils
import org.videolan.vlc.util.ITEM
import org.videolan.vlc.util.isSchemeSupported
import org.videolan.vlc.viewmodels.browser.BrowserModel
import org.videolan.vlc.viewmodels.browser.NetworkModel
import org.videolan.vlc.viewmodels.browser.TYPE_FILE
import org.videolan.vlc.viewmodels.browser.TYPE_NETWORK
import org.videolan.vlc.viewmodels.browser.*
private const val TAG = "FileBrowserTvFragment"
@UseExperimental(ObsoleteCoroutinesApi::class)
......@@ -180,6 +177,8 @@ class FileBrowserTvFragment : BaseBrowserTvFragment(), PathAdapterListener {
override fun showRoot(): Boolean = true
override fun getPathOperationDelegate() = viewModel as IPathOperationDelegate
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
launch {
......
......@@ -41,7 +41,7 @@ const val TYPE_STORAGE = 3L
@ObsoleteCoroutinesApi
@ExperimentalCoroutinesApi
open class BrowserModel(context: Context, val url: String?, type: Long, showHiddenFiles: Boolean, private val showDummyCategory: Boolean) : BaseModel<MediaLibraryItem>(context), TvBrowserModel {
open class BrowserModel(context: Context, val url: String?, type: Long, showHiddenFiles: Boolean, private val showDummyCategory: Boolean) : BaseModel<MediaLibraryItem>(context), TvBrowserModel, IPathOperationDelegate by PathOperationDelegate() {
override var currentItem: MediaLibraryItem? = null
override var nbColumns: Int = 0
......
package org.videolan.vlc.viewmodels.browser
import android.net.Uri
import android.util.Base64
import androidx.collection.SimpleArrayMap
interface IPathOperationDelegate {
// fun getBackstack(from: String, to: String): SimpleArrayMap<String, Uri>
fun appendPathToUri(path: String, uri: Uri.Builder)
fun replaceStoragePath(path: String): String
fun makePathSafe(path: String): String
fun retrieveSafePath(encoded: String): String
}
class PathOperationDelegate : IPathOperationDelegate {
companion object {
val storages = SimpleArrayMap<String, String>()
}
// override fun getBackstack(from: String, to: String): SimpleArrayMap<String, Uri> {
// val result = SimpleArrayMap<String, Uri>()
// val fromUri = if (from == "root") null else Uri.parse(from)
// val toUri = Uri.parse(to)
// if (BuildConfig.DEBUG) Log.d(this::class.java.simpleName, "PathAdapter From: $fromUri")
// if (BuildConfig.DEBUG) Log.d(this::class.java.simpleName, "PathAdapter To: $toUri")
//
//
//
// return result
// }
/**
* Append a path to the Uri from a String
* It takes care of substituting paths stored in [storages] and splitting if the substituted path contains file separators
*
* @param path the path to append
* @param uri the uri the path should be appended to
*
*/
override fun appendPathToUri(path: String, uri: Uri.Builder) {
var newPath = path
for (i in 0..storages.size()) if (storages.valueAt(i) == newPath) newPath = storages.keyAt(i)
newPath.split('/').forEach {
uri.appendPath(it)
}
}
/**
* Substitutes the [storages]keys by the [storages] values
*
* @param path the real path string
* @return the path string with substitutions
*/
override fun replaceStoragePath(path: String): String {
try {
if (storages.size() > 0) for (i in 0..storages.size()) if (path.startsWith(storages.keyAt(i))) return path.replace(storages.keyAt(i), storages.valueAt(i))
} catch (e: IllegalStateException) {
}
return path
}
/**
* Encodes a String to avoid false positive substitusions
*
* @param path the path to encode
* @return the encoded path
*/
override fun makePathSafe(path: String) = Base64.encodeToString(path.toByteArray(), Base64.DEFAULT)
/**
* Decodes a string previously encoded with [makePathSafe]
*
* @param encoded the encoded path string
* @return the decoded path string
*/
override fun retrieveSafePath(encoded: String) = String(Base64.decode(encoded, Base64.DEFAULT))
}
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment