[Android] Change the browser filename sort to a more "natural" one
Nicolas Pomepuy
git at videolan.org
Wed Sep 21 11:55:25 UTC 2022
vlc-android | branch: master | Nicolas Pomepuy <nicolas at videolabs.io> | Tue Sep 20 08:03:10 2022 +0200| [af3831c124c22f5a19c6aa856c490b4dc50e3f4b] | committer: Duncan McNamara
Change the browser filename sort to a more "natural" one
Fixes #2631
> https://code.videolan.org/videolan/vlc-android/commit/af3831c124c22f5a19c6aa856c490b4dc50e3f4b
---
.../org/videolan/vlc/providers/BrowserProvider.kt | 19 ++++---
.../src/org/videolan/vlc/util/Kextensions.kt | 61 ++++++++++++++++++++++
.../src/org/videolan/vlc/util/ModelsHelper.kt | 38 +++++++-------
3 files changed, 92 insertions(+), 26 deletions(-)
diff --git a/application/vlc-android/src/org/videolan/vlc/providers/BrowserProvider.kt b/application/vlc-android/src/org/videolan/vlc/providers/BrowserProvider.kt
index d3d2a1d60..c65704174 100644
--- a/application/vlc-android/src/org/videolan/vlc/providers/BrowserProvider.kt
+++ b/application/vlc-android/src/org/videolan/vlc/providers/BrowserProvider.kt
@@ -74,15 +74,22 @@ abstract class BrowserProvider(val context: Context, val dataset: LiveDataset<Me
val descriptionUpdate = MutableLiveData<Pair<Int, String>>()
internal val medialibrary = Medialibrary.getInstance()
- private val comparator : Comparator<MediaLibraryItem>?
- get() = when {
+ fun isComparatorAboutFilename() = when {
+ Settings.showTvUi && sort == Medialibrary.SORT_ALPHA && desc -> false
+ Settings.showTvUi && sort == Medialibrary.SORT_ALPHA && !desc -> false
+ sort == Medialibrary.SORT_ALPHA && desc -> false
+ sort == Medialibrary.SORT_ALPHA && !desc -> false
+ (sort == Medialibrary.SORT_FILENAME || sort == Medialibrary.SORT_DEFAULT) && desc -> true
+ else -> true
+ }
+ fun getComparator(nbOfDigits: Int): Comparator<MediaLibraryItem>? = when {
Settings.showTvUi && sort == Medialibrary.SORT_ALPHA && desc -> tvDescComp
Settings.showTvUi && sort == Medialibrary.SORT_ALPHA && !desc -> tvAscComp
url != null && Uri.parse(url)?.scheme == "upnp" -> null
sort == Medialibrary.SORT_ALPHA && desc -> descComp
sort == Medialibrary.SORT_ALPHA && !desc -> ascComp
- (sort == Medialibrary.SORT_FILENAME || sort == Medialibrary.SORT_DEFAULT) && desc -> filenameDescComp
- else -> filenameAscComp
+ (sort == Medialibrary.SORT_FILENAME || sort == Medialibrary.SORT_DEFAULT) && desc -> getFilenameDescComp(nbOfDigits)
+ else -> getFilenameAscComp(nbOfDigits)
}
init {
@@ -178,7 +185,7 @@ abstract class BrowserProvider(val context: Context, val dataset: LiveDataset<Me
* @param files the files to sort
*/
fun sort(files: MutableList<MediaLibraryItem>) {
- comparator?.let { files.apply { this.sortWith(it) } } ?: if (desc) files.apply { reverse() }
+ getComparator(if (isComparatorAboutFilename()) files.determineMaxNbOfDigits() else 0)?.let { files.apply { this.sortWith(it) } } ?: if (desc) files.apply { reverse() }
}
suspend fun browseUrl(url: String): List<MediaLibraryItem> {
@@ -232,7 +239,7 @@ abstract class BrowserProvider(val context: Context, val dataset: LiveDataset<Me
}.buffer(Channel.UNLIMITED)
open fun addMedia(media: MediaLibraryItem) {
- comparator?.let { dataset.add(media, it) } ?: dataset.add(media)
+ getComparator(if (isComparatorAboutFilename()) dataset.value.determineMaxNbOfDigits() else 0)?.let { dataset.add(media, it) } ?: dataset.add(media)
}
open fun refresh() {
diff --git a/application/vlc-android/src/org/videolan/vlc/util/Kextensions.kt b/application/vlc-android/src/org/videolan/vlc/util/Kextensions.kt
index 72180a879..6f08794aa 100644
--- a/application/vlc-android/src/org/videolan/vlc/util/Kextensions.kt
+++ b/application/vlc-android/src/org/videolan/vlc/util/Kextensions.kt
@@ -58,6 +58,7 @@ import java.net.URI
import java.net.URISyntaxException
import java.security.SecureRandom
import java.util.*
+import kotlin.math.max
import kotlin.math.roundToInt
fun String.validateLocation(): Boolean {
@@ -381,6 +382,66 @@ fun <T> Flow<T>.launchWhenStarted(scope: LifecycleCoroutineScope): Job = scope.l
collect() // tail-call
}
+/**
+ * Sanitize a string by adding enough "0" at the start
+ * to make a "natural" alphanumeric comparison (1, 2, 10, 11, 20) instead of a strict one (1, 10, 11, 21, 20)
+ *
+ * @param nbOfDigits the number of digits to reach
+ * @return a string having exactly [nbOfDigits] digits at the start
+ */
+fun String?.sanitizeStringForAlphaCompare(nbOfDigits: Int): String? {
+ if (this == null) return null
+ if (first().isDigit()) return buildString {
+ for (i in 0 until (nbOfDigits - (getStartingNumber()?.numberOfDigits() ?: 0))) {
+ append("0")
+ }
+ append(this at sanitizeStringForAlphaCompare)
+ }
+ return this
+}
+
+/**
+ * Calculate the number of digits of an Int
+ *
+ * @return the number of digits of this Int
+ */
+fun Int.numberOfDigits(): Int = when (this) {
+ in -9..9 -> 1
+ else -> 1 + (this / 10).numberOfDigits()
+}
+
+/**
+ * Get the number described at the start of this String if any
+ *
+ * @return the starting number of this String, null if no number found
+ */
+fun String.getStartingNumber(): Int? {
+ return try {
+ buildString {
+ for (c in this at getStartingNumber)
+ if (c.isDigit())
+ append(c)
+ else break
+ }.toInt()
+ } catch (e: NumberFormatException) {
+ null
+ }
+}
+
+/**
+ * Determine the max number of digits iat the start of
+ * this lit items' filename
+ *
+ * @return a max number of digits
+ */
+fun List<MediaLibraryItem>.determineMaxNbOfDigits(): Int {
+ var numberOfPrepending = 0
+ forEach {
+ numberOfPrepending = max((it as? MediaWrapper)?.fileName?.getStartingNumber()?.numberOfDigits()
+ ?: 0, numberOfPrepending)
+ }
+ return numberOfPrepending
+}
fun Fragment.showParentFolder(media: MediaWrapper) {
val parent = MLServiceLocator.getAbstractMediaWrapper(media.uri.retrieveParent()).apply {
diff --git a/application/vlc-android/src/org/videolan/vlc/util/ModelsHelper.kt b/application/vlc-android/src/org/videolan/vlc/util/ModelsHelper.kt
index 01c2c3d3c..3c2245353 100644
--- a/application/vlc-android/src/org/videolan/vlc/util/ModelsHelper.kt
+++ b/application/vlc-android/src/org/videolan/vlc/util/ModelsHelper.kt
@@ -283,26 +283,24 @@ val tvDescComp by lazy {
}
}
-val filenameAscComp by lazy {
- Comparator<MediaLibraryItem> { item1, item2 ->
- val type1 = (item1 as? MediaWrapper)?.type
- val type2 = (item2 as? MediaWrapper)?.type
- if (type1 == MediaWrapper.TYPE_DIR && type2 != MediaWrapper.TYPE_DIR) return at Comparator -1
- else if (type1 != MediaWrapper.TYPE_DIR && type2 == MediaWrapper.TYPE_DIR) return at Comparator 1
- val filename1 = (item1 as? MediaWrapper)?.fileName ?: (item1 as? Storage)?.title
- val filename2 = (item2 as? MediaWrapper)?.fileName ?: (item2 as? Storage)?.title
- filename1?.lowercase(Locale.getDefault())?.compareTo(filename2?.lowercase(Locale.getDefault()) ?: "") ?: -1
- }
+fun getFilenameAscComp(nbOfDigits: Int): Comparator<MediaLibraryItem> = Comparator<MediaLibraryItem> { item1, item2 ->
+ val type1 = (item1 as? MediaWrapper)?.type
+ val type2 = (item2 as? MediaWrapper)?.type
+ if (type1 == MediaWrapper.TYPE_DIR && type2 != MediaWrapper.TYPE_DIR) return at Comparator -1
+ else if (type1 != MediaWrapper.TYPE_DIR && type2 == MediaWrapper.TYPE_DIR) return at Comparator 1
+ val filename1 = (item1 as? MediaWrapper)?.fileName ?: (item1 as? Storage)?.title
+ val filename2 = (item2 as? MediaWrapper)?.fileName ?: (item2 as? Storage)?.title
+ filename1?.lowercase(Locale.getDefault()).sanitizeStringForAlphaCompare(nbOfDigits)?.compareTo(filename2?.lowercase(Locale.getDefault()).sanitizeStringForAlphaCompare(nbOfDigits)
+ ?: "") ?: -1
}
-val filenameDescComp by lazy {
- Comparator<MediaLibraryItem> { item1, item2 ->
- val type1 = (item1 as? MediaWrapper)?.type
- val type2 = (item2 as? MediaWrapper)?.type
- if (type1 == MediaWrapper.TYPE_DIR && type2 != MediaWrapper.TYPE_DIR) return at Comparator -1
- else if (type1 != MediaWrapper.TYPE_DIR && type2 == MediaWrapper.TYPE_DIR) return at Comparator 1
- val filename1 = (item1 as? MediaWrapper)?.fileName ?: (item1 as? Storage)?.title
- val filename2 = (item2 as? MediaWrapper)?.fileName ?: (item2 as? Storage)?.title
- filename2?.lowercase(Locale.getDefault())?.compareTo(filename1?.lowercase(Locale.getDefault()) ?: "") ?: -1
- }
+fun getFilenameDescComp(nbOfDigits: Int): Comparator<MediaLibraryItem> = Comparator<MediaLibraryItem> { item1, item2 ->
+ val type1 = (item1 as? MediaWrapper)?.type
+ val type2 = (item2 as? MediaWrapper)?.type
+ if (type1 == MediaWrapper.TYPE_DIR && type2 != MediaWrapper.TYPE_DIR) return at Comparator -1
+ else if (type1 != MediaWrapper.TYPE_DIR && type2 == MediaWrapper.TYPE_DIR) return at Comparator 1
+ val filename1 = (item1 as? MediaWrapper)?.fileName ?: (item1 as? Storage)?.title
+ val filename2 = (item2 as? MediaWrapper)?.fileName ?: (item2 as? Storage)?.title
+ filename2?.lowercase(Locale.getDefault()).sanitizeStringForAlphaCompare(nbOfDigits)?.compareTo(filename1?.lowercase(Locale.getDefault()).sanitizeStringForAlphaCompare(nbOfDigits)
+ ?: "") ?: -1
}
\ No newline at end of file
More information about the Android
mailing list