Skip to content

Commit c2e0911

Browse files
committed
refactor: optimize query option
1 parent c227c31 commit c2e0911

File tree

18 files changed

+333
-236
lines changed

18 files changed

+333
-236
lines changed

android/src/main/java/com/musiclibrary/MusicLibraryModule.kt

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@ import com.facebook.react.bridge.Promise
55
import com.facebook.react.bridge.ReadableMap
66
import com.facebook.react.bridge.Arguments
77
import com.facebook.react.module.annotations.ReactModule
8-
import com.musiclibrary.utils.ReadableMapMapper.toAssetsOptions
8+
import com.musiclibrary.utils.ReadableMapMapper.toTrackOptions
9+
import com.musiclibrary.utils.ReadableMapMapper.toAlbumOptions
10+
import com.musiclibrary.utils.ReadableMapMapper.toArtistOptions
911
import com.musiclibrary.utils.ModuleUtils.throwUnlessPermissionsGranted
1012
import com.musiclibrary.utils.ModuleUtils.withModuleScope
1113
import com.musiclibrary.tracks.GetTracks
@@ -27,7 +29,7 @@ class MusicLibraryModule(reactContext: ReactApplicationContext) :
2729
override fun getTracksAsync(options: ReadableMap, promise: Promise) {
2830
throwUnlessPermissionsGranted(reactApplicationContext, isWrite = false) {
2931
withModuleScope(promise) {
30-
GetTracks(reactApplicationContext, options.toAssetsOptions(), promise)
32+
GetTracks(reactApplicationContext, options.toTrackOptions(), promise)
3133
.execute()
3234
}
3335
}
@@ -54,7 +56,7 @@ class MusicLibraryModule(reactContext: ReactApplicationContext) :
5456
override fun getTracksByArtistAsync(artistId: String, options: ReadableMap, promise: Promise) {
5557
throwUnlessPermissionsGranted(reactApplicationContext, isWrite = false) {
5658
withModuleScope(promise) {
57-
GetTracksByArtist(reactApplicationContext, artistId, options, promise)
59+
GetTracksByArtist(reactApplicationContext, artistId, options.toTrackOptions(), promise)
5860
.execute()
5961
}
6062
}
@@ -63,7 +65,7 @@ class MusicLibraryModule(reactContext: ReactApplicationContext) :
6365
override fun getAlbumsAsync(options: ReadableMap, promise: Promise) {
6466
throwUnlessPermissionsGranted(reactApplicationContext, isWrite = false) {
6567
withModuleScope(promise) {
66-
GetAlbums(reactApplicationContext, options.toAssetsOptions(), promise)
68+
GetAlbums(reactApplicationContext, options.toAlbumOptions(), promise)
6769
.execute()
6870
}
6971
}
@@ -81,7 +83,7 @@ class MusicLibraryModule(reactContext: ReactApplicationContext) :
8183
override fun getArtistsAsync(options: ReadableMap, promise: Promise) {
8284
throwUnlessPermissionsGranted(reactApplicationContext, isWrite = false) {
8385
withModuleScope(promise) {
84-
GetArtists(reactApplicationContext, options.toAssetsOptions(), promise)
86+
GetArtists(reactApplicationContext, options.toArtistOptions(), promise)
8587
.execute()
8688
}
8789
}

android/src/main/java/com/musiclibrary/albums/GetAlbums.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,12 @@ package com.musiclibrary.albums
22

33
import android.content.Context
44
import com.facebook.react.bridge.Promise
5-
import com.musiclibrary.models.AssetsOptions
5+
import com.musiclibrary.models.AlbumOptions
66
import com.musiclibrary.utils.DataConverter
77

88
internal class GetAlbums(
99
private val context: Context,
10-
private val options: AssetsOptions,
10+
private val options: AlbumOptions,
1111
private val promise: Promise
1212
) {
1313

android/src/main/java/com/musiclibrary/albums/GetAlbumsQuery.kt

Lines changed: 7 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import androidx.core.net.toUri
1010
object GetAlbumsQuery {
1111
fun getAlbums(
1212
contentResolver: ContentResolver,
13-
options: AssetsOptions,
13+
options: AlbumOptions,
1414
): PaginatedResult<Album> {
1515
val projection = arrayOf(
1616
MediaStore.Audio.Albums._ID,
@@ -114,38 +114,17 @@ object GetAlbumsQuery {
114114
)
115115
}
116116

117-
private fun buildSelection(options: AssetsOptions): String {
117+
private fun buildSelection(options: AlbumOptions): String {
118118
val conditions = mutableListOf<String>()
119119

120120
// Only query albums that have tracks
121121
conditions.add("${MediaStore.Audio.Albums.NUMBER_OF_SONGS} > 0")
122122

123-
// Directory filtering for albums is more complex since albums don't have direct path
124-
// We'll need to filter based on tracks in the album
125-
if (!options.directory.isNullOrEmpty()) {
126-
// Filter albums that have tracks in the specified directory
127-
conditions.add("EXISTS (SELECT 1 FROM ${MediaStore.Audio.Media.EXTERNAL_CONTENT_URI} WHERE ${MediaStore.Audio.Media.ALBUM_ID} = ${MediaStore.Audio.Albums._ID} AND ${MediaStore.Audio.Media.DATA} LIKE ?)")
128-
}
129-
130123
return conditions.joinToString(" AND ")
131124
}
132125

133-
private fun buildSelectionArgs(options: AssetsOptions): Array<String>? {
134-
val args = mutableListOf<String>()
135-
136-
if (!options.directory.isNullOrEmpty()) {
137-
val dir = if (options.directory.startsWith("content://")) {
138-
uriToFullPath(options.directory.toUri())
139-
} else {
140-
options.directory
141-
}
142-
143-
if (!dir.isNullOrEmpty()) {
144-
args.add("$dir%")
145-
}
146-
}
147-
148-
return if (args.isEmpty()) null else args.toTypedArray()
126+
private fun buildSelectionArgs(options: AlbumOptions): Array<String>? {
127+
return null
149128
}
150129

151130
private fun uriToFullPath(treeUri: Uri): String? {
@@ -173,11 +152,11 @@ object GetAlbumsQuery {
173152

174153
val column = when (parts[0].lowercase()) {
175154
"default" -> MediaStore.Audio.Albums.ALBUM
155+
"title" -> MediaStore.Audio.Albums.ALBUM
176156
"artist" -> MediaStore.Audio.Albums.ARTIST
177-
"album" -> MediaStore.Audio.Albums.ALBUM
178-
"track_count" -> MediaStore.Audio.Albums.NUMBER_OF_SONGS
157+
"trackcount" -> MediaStore.Audio.Albums.NUMBER_OF_SONGS
179158
"year" -> MediaStore.Audio.Albums.FIRST_YEAR
180-
else -> throw IllegalArgumentException("Unsupported SortKey: ${parts[0]}")
159+
else -> throw IllegalArgumentException("Unsupported SortKey for albums: ${parts[0]}")
181160
}
182161

183162
val order = parts[1].uppercase()

android/src/main/java/com/musiclibrary/artists/GetArtists.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,12 @@ package com.musiclibrary.artists
22

33
import android.content.Context
44
import com.facebook.react.bridge.Promise
5-
import com.musiclibrary.models.AssetsOptions
5+
import com.musiclibrary.models.ArtistOptions
66
import com.musiclibrary.utils.DataConverter
77

88
internal class GetArtists(
99
private val context: Context,
10-
private val options: AssetsOptions,
10+
private val options: ArtistOptions,
1111
private val promise: Promise
1212
) {
1313

android/src/main/java/com/musiclibrary/artists/GetArtistsQuery.kt

Lines changed: 8 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import androidx.core.net.toUri
1010
object GetArtistsQuery {
1111
fun getArtists(
1212
contentResolver: ContentResolver,
13-
options: AssetsOptions,
13+
options: ArtistOptions,
1414
): PaginatedResult<Artist> {
1515
val projection = arrayOf(
1616
MediaStore.Audio.Artists._ID,
@@ -106,52 +106,17 @@ object GetArtistsQuery {
106106
)
107107
}
108108

109-
private fun buildSelection(options: AssetsOptions): String {
109+
private fun buildSelection(options: ArtistOptions): String {
110110
val conditions = mutableListOf<String>()
111111

112112
// Only query artists that have tracks
113113
conditions.add("${MediaStore.Audio.Artists.NUMBER_OF_TRACKS} > 0")
114114

115-
// Directory filtering for artists is more complex since artists don't have direct path
116-
// We'll need to filter based on tracks by the artist
117-
if (!options.directory.isNullOrEmpty()) {
118-
// Filter artists that have tracks in the specified directory
119-
conditions.add("EXISTS (SELECT 1 FROM ${MediaStore.Audio.Media.EXTERNAL_CONTENT_URI} WHERE ${MediaStore.Audio.Media.ARTIST_ID} = ${MediaStore.Audio.Artists._ID} AND ${MediaStore.Audio.Media.DATA} LIKE ?)")
120-
}
121-
122115
return conditions.joinToString(" AND ")
123116
}
124117

125-
private fun buildSelectionArgs(options: AssetsOptions): Array<String>? {
126-
val args = mutableListOf<String>()
127-
128-
if (!options.directory.isNullOrEmpty()) {
129-
val dir = if (options.directory.startsWith("content://")) {
130-
uriToFullPath(options.directory.toUri())
131-
} else {
132-
options.directory
133-
}
134-
135-
if (!dir.isNullOrEmpty()) {
136-
args.add("$dir%")
137-
}
138-
}
139-
140-
return if (args.isEmpty()) null else args.toTypedArray()
141-
}
142-
143-
private fun uriToFullPath(treeUri: Uri): String? {
144-
val docId = DocumentsContract.getTreeDocumentId(treeUri) // "primary:Music/abc"
145-
val parts = docId.split(":")
146-
if (parts.size < 2) return null
147-
148-
val type = parts[0]
149-
val relativePath = parts[1]
150-
151-
return when (type) {
152-
"primary" -> "/storage/emulated/0/$relativePath"
153-
else -> "/storage/$type/$relativePath"
154-
}
118+
private fun buildSelectionArgs(options: ArtistOptions): Array<String>? {
119+
return null
155120
}
156121

157122
private fun buildSortOrder(sortBy: List<String>): String {
@@ -165,10 +130,10 @@ object GetArtistsQuery {
165130

166131
val column = when (parts[0].lowercase()) {
167132
"default" -> MediaStore.Audio.Artists.ARTIST
168-
"artist" -> MediaStore.Audio.Artists.ARTIST
169-
"album_count" -> MediaStore.Audio.Artists.NUMBER_OF_ALBUMS
170-
"track_count" -> MediaStore.Audio.Artists.NUMBER_OF_TRACKS
171-
else -> throw IllegalArgumentException("Unsupported SortKey: ${parts[0]}")
133+
"title" -> MediaStore.Audio.Artists.ARTIST
134+
"trackcount" -> MediaStore.Audio.Artists.NUMBER_OF_TRACKS
135+
"albumcount" -> MediaStore.Audio.Artists.NUMBER_OF_ALBUMS
136+
else -> throw IllegalArgumentException("Unsupported SortKey for artists: ${parts[0]}")
172137
}
173138

174139
val order = parts[1].uppercase()
Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,23 @@
11
package com.musiclibrary.models
22

3-
data class AssetsOptions(
3+
// Track-specific options
4+
data class TrackOptions(
45
val after: String? = null,
56
val first: Int,
67
val sortBy: List<String>,
78
val directory: String? = null,
89
)
10+
11+
// Album-specific options
12+
data class AlbumOptions(
13+
val after: String? = null,
14+
val first: Int,
15+
val sortBy: List<String>,
16+
)
17+
18+
// Artist-specific options
19+
data class ArtistOptions(
20+
val after: String? = null,
21+
val first: Int,
22+
val sortBy: List<String>,
23+
)

android/src/main/java/com/musiclibrary/tracks/GetTracks.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,12 @@ package com.musiclibrary.tracks
22

33
import android.content.Context
44
import com.facebook.react.bridge.Promise
5-
import com.musiclibrary.models.AssetsOptions
5+
import com.musiclibrary.models.TrackOptions
66
import com.musiclibrary.utils.DataConverter
77

88
internal class GetTracks(
99
private val context: Context,
10-
private val options: AssetsOptions,
10+
private val options: TrackOptions,
1111
private val promise: Promise
1212
) {
1313

android/src/main/java/com/musiclibrary/tracks/GetTracksByArtist.kt

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,23 +2,20 @@ package com.musiclibrary.tracks
22

33
import android.content.Context
44
import com.facebook.react.bridge.Promise
5-
import com.facebook.react.bridge.ReadableMap
6-
import com.musiclibrary.models.AssetsOptions
5+
import com.musiclibrary.models.TrackOptions
76
import com.musiclibrary.utils.DataConverter
8-
import com.musiclibrary.utils.ReadableMapMapper.toAssetsOptions
97

108
internal class GetTracksByArtist(
119
private val context: Context,
1210
private val artistId: String,
13-
private val options: ReadableMap,
11+
private val options: TrackOptions,
1412
private val promise: Promise
1513
) {
1614

1715
fun execute() {
1816
try {
1917
val contentResolver = context.contentResolver
20-
val assetsOptions = options.toAssetsOptions()
21-
val result = GetTracksByArtistQuery.getTracksByArtist(contentResolver, artistId, assetsOptions)
18+
val result = GetTracksByArtistQuery.getTracksByArtist(contentResolver, artistId, options)
2219

2320
// Convert result to React Native bridge format
2421
val resultMap = DataConverter.paginatedResultToWritableMap(result) { track ->
@@ -30,4 +27,4 @@ internal class GetTracksByArtist(
3027
promise.reject("QUERY_ERROR", "Failed to query tracks by artist: ${e.message}", e)
3128
}
3229
}
33-
}
30+
}

android/src/main/java/com/musiclibrary/tracks/GetTracksByArtistQuery.kt

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ object GetTracksByArtistQuery {
88
fun getTracksByArtist(
99
contentResolver: ContentResolver,
1010
artistId: String,
11-
options: AssetsOptions,
11+
options: TrackOptions,
1212
): PaginatedResult<Track> {
1313
val projection = arrayOf(
1414
MediaStore.Audio.Media._ID,
@@ -24,7 +24,7 @@ object GetTracksByArtistQuery {
2424

2525
val selection = "${MediaStore.Audio.Media.ARTIST_ID} = ? AND ${MediaStore.Audio.Media.IS_MUSIC} = 1 AND ${MediaStore.Audio.Media.DURATION} > 0"
2626
val selectionArgs = arrayOf(artistId)
27-
val sortOrder = "${MediaStore.Audio.Media.ALBUM} ASC, ${MediaStore.Audio.Media.TRACK} ASC, ${MediaStore.Audio.Media.TITLE} ASC"
27+
val sortOrder = buildSortOrder(options.sortBy)
2828

2929
val cursor = contentResolver.query(
3030
MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
@@ -124,4 +124,32 @@ object GetTracksByArtistQuery {
124124
totalCount = totalCount
125125
)
126126
}
127-
}
127+
128+
private fun buildSortOrder(sortBy: List<String>): String {
129+
if (sortBy.isEmpty()) {
130+
return "${MediaStore.Audio.Media.TITLE} ASC"
131+
}
132+
133+
return sortBy.joinToString(", ") { sortOption ->
134+
val parts = sortOption.split(" ")
135+
require(parts.size == 2) { "sortBy should be 'key order'" }
136+
137+
val column = when (parts[0].lowercase()) {
138+
"default" -> MediaStore.Audio.Media.TITLE
139+
"title" -> MediaStore.Audio.Media.TITLE
140+
"artist" -> MediaStore.Audio.Media.ARTIST
141+
"album" -> MediaStore.Audio.Media.ALBUM
142+
"duration" -> MediaStore.Audio.Media.DURATION
143+
"createdat" -> MediaStore.Audio.Media.DATE_ADDED
144+
"modifiedat" -> MediaStore.Audio.Media.DATE_MODIFIED
145+
"filesize" -> MediaStore.Audio.Media.SIZE
146+
else -> throw IllegalArgumentException("Unsupported SortKey for tracks: ${parts[0]}")
147+
}
148+
149+
val order = parts[1].uppercase()
150+
require(order == "ASC" || order == "DESC") { "Sort By must be ASC or DESC" }
151+
152+
"$column $order"
153+
}
154+
}
155+
}

0 commit comments

Comments
 (0)