Skip to content

Commit e8d3717

Browse files
manum45manum45
andauthored
Add reverse sort order option (#1018)
* add reverse sorting option * cleanup * go back to old menu style * remove unused parameter, move reverse order to top of sort menu * readd exception in case of unsupported sort type * fix review findings --------- Co-authored-by: manum45 <manu.707@web.de>
1 parent 2947b5b commit e8d3717

7 files changed

Lines changed: 107 additions & 28 deletions

File tree

app/src/main/java/org/akanework/gramophone/ui/adapters/BaseDecorAdapter.kt

Lines changed: 43 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -107,18 +107,16 @@ open class BaseDecorAdapter<T : AdapterFragment.BaseInterface<*>>(
107107
popupMenu.menu.findItem(it.key).isVisible = adapter.canChangeLayout
108108
}
109109
popupMenu.menu.findItem(R.id.display).isVisible = adapter.canChangeLayout
110-
if (adapter.sortType.value != Sorter.Type.None) {
111-
when (adapter.sortType.value) {
112-
in buttonMap.values -> {
113-
popupMenu.menu.findItem(
114-
buttonMap.entries
115-
.first { it.value == adapter.sortType.value }.key
116-
).isChecked = true
117-
}
118110

119-
else -> throw IllegalStateException("Invalid sortType ${adapter.sortType.value.name}")
120-
}
111+
val currentSort = adapter.sortType.value
112+
val activeEntry = buttonMap.entries.find { it.value == currentSort || Sorter.Type.inverse(it.value) == currentSort }
113+
114+
if (activeEntry == null) {
115+
throw IllegalStateException("Invalid sortType ${adapter.sortType.value.name}")
121116
}
117+
118+
popupMenu.menu.findItem(activeEntry.key).isChecked = true
119+
122120
if (adapter.canChangeLayout) {
123121
when (adapter.layoutType) {
124122
in layoutMap.values -> {
@@ -131,16 +129,29 @@ open class BaseDecorAdapter<T : AdapterFragment.BaseInterface<*>>(
131129
else -> throw IllegalStateException("Invalid layoutType ${adapter.layoutType?.name}")
132130
}
133131
}
132+
133+
val reverseItem = popupMenu.menu.findItem(R.id.reverse_order)
134+
val inverse = Sorter.Type.inverse(adapter.sortType.value)
135+
if (inverse == null) {
136+
reverseItem.isVisible = false
137+
} else {
138+
reverseItem.isChecked = currentSort != activeEntry.value && currentSort != Sorter.Type.None
139+
}
140+
134141
popupMenu.setOnMenuItemClickListener { menuItem ->
135142
when (menuItem.itemId) {
136143
in buttonMap.keys -> {
137144
if (!menuItem.isChecked) {
138-
adapter.sort(buttonMap[menuItem.itemId]!!)
145+
// always use default direction for this sort mode if sort mode is changed,
146+
// and reset the reverseOrder checkbox
147+
val targetType = buttonMap[menuItem.itemId]!!
148+
reverseItem.isChecked = false
149+
adapter.sort(targetType)
139150
menuItem.isChecked = true
140151
prefs.edit {
141152
putString(
142153
"S" + getAdapterType(adapter).toString(),
143-
buttonMap[menuItem.itemId].toString()
154+
targetType.toString()
144155
)
145156
}
146157
}
@@ -161,6 +172,25 @@ open class BaseDecorAdapter<T : AdapterFragment.BaseInterface<*>>(
161172
true
162173
}
163174

175+
R.id.reverse_order -> {
176+
menuItem.isChecked = !menuItem.isChecked
177+
val activeId = buttonMap.entries.find {
178+
it.value == adapter.sortType.value || Sorter.Type.inverse(it.value) == adapter.sortType.value
179+
}?.key ?: -1
180+
val baseType = buttonMap[activeId]
181+
if (baseType != null) {
182+
val targetType = if (menuItem.isChecked) Sorter.Type.inverse(baseType) ?: baseType else baseType
183+
adapter.sort(targetType)
184+
prefs.edit {
185+
putString(
186+
"S" + getAdapterType(adapter).toString(),
187+
targetType.toString()
188+
)
189+
}
190+
}
191+
true
192+
}
193+
164194
else -> onExtraMenuButtonPressed(menuItem)
165195
}
166196
}
@@ -321,4 +351,4 @@ open class BaseDecorAdapter<T : AdapterFragment.BaseInterface<*>>(
321351
override fun getItemHeightFromZeroTo(to: Int): Int {
322352
return if (to > 0) dpHeight else 0
323353
}
324-
}
354+
}

app/src/main/java/org/akanework/gramophone/ui/adapters/DetailedFolderAdapter.kt

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -92,8 +92,10 @@ class DetailedFolderAdapter(
9292
Sorter.Type.None
9393
}
9494
override val sortTypes = setOf(
95-
Sorter.Type.ByFilePathAscending, Sorter.Type.BySizeDescending,
96-
Sorter.Type.ByAddDateDescending, Sorter.Type.ByModifiedDateDescending
95+
Sorter.Type.ByFilePathAscending, Sorter.Type.ByFilePathDescending,
96+
Sorter.Type.BySizeDescending, Sorter.Type.BySizeAscending,
97+
Sorter.Type.ByAddDateDescending, Sorter.Type.ByAddDateAscending,
98+
Sorter.Type.ByModifiedDateDescending, Sorter.Type.ByModifiedDateAscending
9799
)
98100
override val sortType = MutableStateFlow(
99101
if (prefSortType != Sorter.Type.None && sortTypes.contains(prefSortType))
@@ -138,14 +140,32 @@ class DetailedFolderAdapter(
138140
it.folderList.size + it.songList.size
139141
}
140142

143+
Sorter.Type.BySizeAscending -> item.folderList.values.sortedBy {
144+
it.folderList.size + it.songList.size
145+
}
146+
141147
Sorter.Type.ByAddDateDescending -> item.folderList.values.sortedByDescending {
142148
it.addDate ?: Long.MIN_VALUE
143149
}
144150

151+
Sorter.Type.ByAddDateAscending -> item.folderList.values.sortedBy {
152+
it.addDate ?: Long.MIN_VALUE
153+
}
154+
145155
Sorter.Type.ByModifiedDateDescending -> item.folderList.values.sortedByDescending {
146156
it.modifiedDate ?: Long.MIN_VALUE
147157
}
148158

159+
Sorter.Type.ByModifiedDateAscending -> item.folderList.values.sortedBy {
160+
it.modifiedDate ?: Long.MIN_VALUE
161+
}
162+
163+
Sorter.Type.ByFilePathDescending -> item.folderList.values.sortedWith(
164+
SupportComparator.createAlphanumericComparator(inverted = true, cnv = {
165+
it.folderName
166+
})
167+
)
168+
149169
else -> item.folderList.values.sortedWith(
150170
SupportComparator.createAlphanumericComparator(cnv = {
151171
it.folderName

app/src/main/java/org/akanework/gramophone/ui/adapters/SongAdapter.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -446,4 +446,4 @@ class SongAdapter(
446446
return item.mediaMetadata.modifiedDate ?: -1
447447
}
448448
}
449-
}
449+
}

app/src/main/java/org/akanework/gramophone/ui/adapters/Sorter.kt

Lines changed: 32 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -100,11 +100,11 @@ class Sorter<T>(
100100
ByAlbumYearDescending, ByAlbumYearAscending,
101101
BySizeDescending, BySizeAscending,
102102
ByAlbumSizeDescending, ByAlbumSizeAscending,
103-
NaturalOrder, ByAddDateDescending, ByAddDateAscending,
103+
NaturalOrder, NaturalOrderDescending, ByAddDateDescending, ByAddDateAscending,
104104
ByReleaseDateDescending, ByReleaseDateAscending,
105105
ByModifiedDateDescending, ByModifiedDateAscending,
106106
ByFilePathDescending, ByFilePathAscending,
107-
ByDiscAndTrack,
107+
ByDiscAndTrack, ByDiscAndTrackDescending,
108108
None;
109109

110110
companion object {
@@ -127,7 +127,8 @@ class Sorter<T>(
127127
BySizeAscending -> BySizeDescending
128128
ByAlbumSizeDescending -> ByAlbumSizeAscending
129129
ByAlbumSizeAscending -> ByAlbumSizeDescending
130-
NaturalOrder -> null
130+
NaturalOrder -> NaturalOrderDescending
131+
NaturalOrderDescending -> NaturalOrder
131132
ByAddDateDescending -> ByAddDateAscending
132133
ByAddDateAscending -> ByAddDateDescending
133134
ByReleaseDateDescending -> ByReleaseDateAscending
@@ -136,17 +137,26 @@ class Sorter<T>(
136137
ByModifiedDateAscending -> ByModifiedDateDescending
137138
ByFilePathDescending -> ByFilePathAscending
138139
ByFilePathAscending -> ByFilePathDescending
139-
ByDiscAndTrack -> null
140+
ByDiscAndTrack -> ByDiscAndTrackDescending
141+
ByDiscAndTrackDescending -> ByDiscAndTrack
140142
None -> null
141143
}
142144
}
143145
}
144146

145147
fun getSupportedTypes(): Set<Type> {
146148
return sortingHelper.typesSupported.let { types ->
149+
var res = types
147150
if (naturalOrderHelper != null || rawOrderExposed == Type.NaturalOrder)
148-
types + Type.NaturalOrder
149-
else types
151+
res = res + Type.NaturalOrder
152+
153+
// Automatically add inverse for everything supported
154+
val allWithInverses = mutableSetOf<Type>()
155+
res.forEach {
156+
allWithInverses.add(it)
157+
Type.inverse(it)?.let { inv -> allWithInverses.add(inv) }
158+
}
159+
allWithInverses
150160
}
151161
}
152162

@@ -343,12 +353,24 @@ class Sorter<T>(
343353
compareBy { sortingHelper.getDiscAndTrack(it) }
344354
}
345355

356+
Type.ByDiscAndTrackDescending -> {
357+
SupportComparator.createInversionComparator(
358+
compareBy { sortingHelper.getDiscAndTrack(it) }, true
359+
)
360+
}
361+
346362
Type.NaturalOrder -> {
347363
SupportComparator.createInversionComparator(
348364
compareBy { naturalOrderHelper!!.lookup(it) }, false
349365
)
350366
}
351367

368+
Type.NaturalOrderDescending -> {
369+
SupportComparator.createInversionComparator(
370+
compareBy { naturalOrderHelper!!.lookup(it) }, true
371+
)
372+
}
373+
352374
Type.None -> throw IllegalStateException()
353375
}
354376
)
@@ -396,7 +418,7 @@ class Sorter<T>(
396418
sortingHelper.getAlbumSize(item).toString()
397419
}
398420

399-
Type.ByDiscAndTrack -> {
421+
Type.ByDiscAndTrack, Type.ByDiscAndTrackDescending -> {
400422
sortingHelper.getDiscAndTrack(item).toString()
401423
}
402424

@@ -409,11 +431,11 @@ class Sorter<T>(
409431
}
410432

411433
Type.ByModifiedDateDescending, Type.ByModifiedDateAscending -> {
412-
CalculationUtils.convertUnixTimestampToMonthDay(sortingHelper.getAddDate(item))
434+
CalculationUtils.convertUnixTimestampToMonthDay(sortingHelper.getModifiedDate(item))
413435
}
414436

415-
Type.NaturalOrder -> {
416-
(if (rawOrderExposed == sortType) {
437+
Type.NaturalOrder, Type.NaturalOrderDescending -> {
438+
(if (rawOrderExposed == sortType || (rawOrderExposed != null && Type.inverse(rawOrderExposed) == sortType)) {
417439
pos
418440
} else {
419441
naturalOrderHelper!!.lookup(item)

app/src/main/java/uk/akane/libphonograph/items/FileNode.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ interface FileNode {
3232
if (it == Long.MAX_VALUE) null else it
3333
}
3434
val modifiedDate: Long?
35-
get() = min(songList.maxOfOrNull { it.mediaMetadata.modifiedDate ?: Long.MIN_VALUE }
35+
get() = maxOf(songList.maxOfOrNull { it.mediaMetadata.modifiedDate ?: Long.MIN_VALUE }
3636
?: Long.MIN_VALUE,
3737
folderList.maxOfOrNull { it.value.modifiedDate ?: Long.MIN_VALUE }
3838
?: Long.MIN_VALUE).let {

app/src/main/res/menu/sort_menu.xml

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,11 @@
22
<menu xmlns:android="http://schemas.android.com/apk/res/android"
33
xmlns:app="http://schemas.android.com/apk/res-auto"
44
xmlns:tools="http://schemas.android.com/tools">
5+
<item
6+
android:id="@+id/reverse_order"
7+
android:checkable="true"
8+
android:title="@string/reverse_order"
9+
app:showAsAction="never" />
510
<group android:checkableBehavior="single">
611
<item
712
android:id="@+id/album_artist_checkbox"
@@ -108,4 +113,4 @@
108113
</group>
109114
</menu>
110115
</item>
111-
</menu>
116+
</menu>

app/src/main/res/values/strings.xml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,8 @@
153153
<string name="folders">Folders</string>
154154
<!-- Color theme in appearance settings: light/dark/follow system -->
155155
<string name="settings_app_theme">App theme</string>
156+
<!-- Switch that reverses the order of the selected sort mode -->
157+
<string name="reverse_order">Reverse order</string>
156158
<!-- The sort mode if something has inherent proper order (playlist order for playlist, CD order
157159
for album, etc) -->
158160
<string name="natural_order">Natural order</string>

0 commit comments

Comments
 (0)