diff --git a/app/src/main/java/code/name/monkey/retromusic/helper/SearchQueryHelper.kt b/app/src/main/java/code/name/monkey/retromusic/helper/SearchQueryHelper.kt index d25b32c0..70d5731c 100644 --- a/app/src/main/java/code/name/monkey/retromusic/helper/SearchQueryHelper.kt +++ b/app/src/main/java/code/name/monkey/retromusic/helper/SearchQueryHelper.kt @@ -19,15 +19,12 @@ import android.os.Bundle import android.provider.MediaStore import code.name.monkey.retromusic.model.Song import code.name.monkey.retromusic.repository.RealSongRepository +import code.name.monkey.retromusic.repository.SongQuery import org.koin.core.component.KoinComponent import org.koin.core.component.inject -import java.util.* +import software.lavender.music.query.StringFilter object SearchQueryHelper : KoinComponent { - private const val TITLE_SELECTION = "lower(" + MediaStore.Audio.AudioColumns.TITLE + ") = ?" - private const val ALBUM_SELECTION = "lower(" + MediaStore.Audio.AudioColumns.ALBUM + ") = ?" - private const val ARTIST_SELECTION = "lower(" + MediaStore.Audio.AudioColumns.ARTIST + ") = ?" - private const val AND = " AND " private val songRepository by inject() var songs = ArrayList() @@ -41,13 +38,10 @@ object SearchQueryHelper : KoinComponent { var songs = listOf() if (artistName != null && albumName != null && titleName != null) { songs = songRepository.songs( - songRepository.makeSongCursor( - ARTIST_SELECTION + AND + ALBUM_SELECTION + AND + TITLE_SELECTION, - arrayOf( - artistName.lowercase(), - albumName.lowercase(), - titleName.lowercase() - ) + SongQuery( + artistName = StringFilter(eq = artistName.lowercase(), lower = true), + albumName = StringFilter(eq = albumName.lowercase(), lower = true), + title = StringFilter(eq = titleName.lowercase(), lower = true) ) ) } @@ -56,12 +50,9 @@ object SearchQueryHelper : KoinComponent { } if (artistName != null && titleName != null) { songs = songRepository.songs( - songRepository.makeSongCursor( - ARTIST_SELECTION + AND + TITLE_SELECTION, - arrayOf( - artistName.lowercase(), - titleName.lowercase() - ) + SongQuery( + artistName = StringFilter(eq = artistName.lowercase(), lower = true), + title = StringFilter(eq = titleName.lowercase(), lower = true) ) ) } @@ -70,12 +61,9 @@ object SearchQueryHelper : KoinComponent { } if (albumName != null && titleName != null) { songs = songRepository.songs( - songRepository.makeSongCursor( - ALBUM_SELECTION + AND + TITLE_SELECTION, - arrayOf( - albumName.lowercase(), - titleName.lowercase() - ) + SongQuery( + albumName = StringFilter(eq = albumName.lowercase(), lower = true), + title = StringFilter(eq = titleName.lowercase(), lower = true) ) ) } @@ -84,10 +72,7 @@ object SearchQueryHelper : KoinComponent { } if (artistName != null) { songs = songRepository.songs( - songRepository.makeSongCursor( - ARTIST_SELECTION, - arrayOf(artistName.lowercase()) - ) + SongQuery(artistName = StringFilter(eq = artistName.lowercase(), lower = true)) ) } if (songs.isNotEmpty()) { @@ -95,10 +80,7 @@ object SearchQueryHelper : KoinComponent { } if (albumName != null) { songs = songRepository.songs( - songRepository.makeSongCursor( - ALBUM_SELECTION, - arrayOf(albumName.lowercase()) - ) + SongQuery(albumName = StringFilter(eq = albumName.lowercase(), lower = true)) ) } if (songs.isNotEmpty()) { @@ -106,42 +88,28 @@ object SearchQueryHelper : KoinComponent { } if (titleName != null) { songs = songRepository.songs( - songRepository.makeSongCursor( - TITLE_SELECTION, - arrayOf(titleName.lowercase()) - ) + SongQuery(title = StringFilter(eq = titleName.lowercase(), lower = true)) ) } if (songs.isNotEmpty()) { return songs } songs = songRepository.songs( - songRepository.makeSongCursor( - ARTIST_SELECTION, - arrayOf(query.lowercase()) - ) + SongQuery(artistName = StringFilter(eq = query.lowercase(), lower = true)) ) if (songs.isNotEmpty()) { return songs } songs = songRepository.songs( - songRepository.makeSongCursor( - ALBUM_SELECTION, - arrayOf(query.lowercase()) - ) + SongQuery(albumName = StringFilter(eq = query.lowercase(), lower = true)) ) if (songs.isNotEmpty()) { return songs } songs = songRepository.songs( - songRepository.makeSongCursor( - TITLE_SELECTION, - arrayOf(query.lowercase()) - ) + SongQuery(title = StringFilter(eq = query.lowercase(), lower = true)) ) - return if (songs.isNotEmpty()) { - songs - } else ArrayList() + return songs.ifEmpty { ArrayList() } } } diff --git a/app/src/main/java/code/name/monkey/retromusic/providers/MusicPlaybackQueueStore.java b/app/src/main/java/code/name/monkey/retromusic/providers/MusicPlaybackQueueStore.java index 6875083b..9151fce9 100644 --- a/app/src/main/java/code/name/monkey/retromusic/providers/MusicPlaybackQueueStore.java +++ b/app/src/main/java/code/name/monkey/retromusic/providers/MusicPlaybackQueueStore.java @@ -20,16 +20,14 @@ import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; import android.provider.BaseColumns; import android.provider.MediaStore.Audio.AudioColumns; - import androidx.annotation.NonNull; import androidx.annotation.Nullable; - -import java.util.List; - import code.name.monkey.retromusic.App; import code.name.monkey.retromusic.model.Song; import code.name.monkey.retromusic.repository.RealSongRepository; +import java.util.List; + /** * @author Andrew Neal, modified for Phonograph by Karim Abou Zeid *

This keeps track of the music playback and history state of the playback service @@ -157,8 +155,9 @@ public class MusicPlaybackQueueStore extends SQLiteOpenHelper { @NonNull private List getQueue(@NonNull final String tableName) { + // TODO: add back cursor support after all just for this.... Cursor cursor = getReadableDatabase().query(tableName, null, null, null, null, null, null); - return new RealSongRepository(App.Companion.getContext()).songs(cursor); + return new RealSongRepository(App.Companion.getContext()).songs(); } /** diff --git a/app/src/main/java/code/name/monkey/retromusic/repository/AlbumRepository.kt b/app/src/main/java/code/name/monkey/retromusic/repository/AlbumRepository.kt index ce023206..a65722cd 100644 --- a/app/src/main/java/code/name/monkey/retromusic/repository/AlbumRepository.kt +++ b/app/src/main/java/code/name/monkey/retromusic/repository/AlbumRepository.kt @@ -14,11 +14,12 @@ package code.name.monkey.retromusic.repository -import android.provider.MediaStore.Audio.AudioColumns import code.name.monkey.retromusic.helper.SortOrder import code.name.monkey.retromusic.model.Album import code.name.monkey.retromusic.model.Song import code.name.monkey.retromusic.util.PreferenceUtil +import software.lavender.music.query.LongFilter +import software.lavender.music.query.StringFilter /** @@ -36,34 +37,24 @@ class RealAlbumRepository(private val songRepository: RealSongRepository) : AlbumRepository { override fun albums(): List { + // TODO: song loader sort order val songs = songRepository.songs( - songRepository.makeSongCursor( - null, - null, - getSongLoaderSortOrder() - ) + SongQuery() ) return splitIntoAlbums(songs) } override fun albums(query: String): List { + // TODO: song loader sort order val songs = songRepository.songs( - songRepository.makeSongCursor( - AudioColumns.ALBUM + " LIKE ?", - arrayOf("%$query%"), - getSongLoaderSortOrder() - ) + SongQuery(albumName = StringFilter(like = "%$query%")) ) return splitIntoAlbums(songs) } override fun album(albumId: Long): Album { - val cursor = songRepository.makeSongCursor( - AudioColumns.ALBUM_ID + "=?", - arrayOf(albumId.toString()), - getSongLoaderSortOrder() - ) - val songs = songRepository.songs(cursor) + // TODO: song loader sort order + val songs = songRepository.songs(SongQuery(albumId = LongFilter(eq = albumId))) val album = Album(albumId, songs) sortAlbumSongs(album) return album diff --git a/app/src/main/java/code/name/monkey/retromusic/repository/ArtistRepository.kt b/app/src/main/java/code/name/monkey/retromusic/repository/ArtistRepository.kt index 9ac1d67b..b77cbe62 100644 --- a/app/src/main/java/code/name/monkey/retromusic/repository/ArtistRepository.kt +++ b/app/src/main/java/code/name/monkey/retromusic/repository/ArtistRepository.kt @@ -14,12 +14,11 @@ package code.name.monkey.retromusic.repository -import android.provider.MediaStore.Audio.AudioColumns -import code.name.monkey.retromusic.ALBUM_ARTIST -import code.name.monkey.retromusic.helper.SortOrder import code.name.monkey.retromusic.model.Album import code.name.monkey.retromusic.model.Artist import code.name.monkey.retromusic.util.PreferenceUtil +import software.lavender.music.query.LongFilter +import software.lavender.music.query.StringFilter interface ArtistRepository { fun artists(): List @@ -49,24 +48,19 @@ class RealArtistRepository( override fun artist(artistId: Long): Artist { if (artistId == Artist.VARIOUS_ARTISTS_ID) { // Get Various Artists + // TODO: song loader sort order + // TODO: why is the filtering done AFTER the query here? val songs = songRepository.songs( - songRepository.makeSongCursor( - null, - null, - getSongLoaderSortOrder() - ) + SongQuery() ) val albums = albumRepository.splitIntoAlbums(songs) .filter { it.albumArtist == Artist.VARIOUS_ARTISTS_DISPLAY_NAME } return Artist(Artist.VARIOUS_ARTISTS_ID, albums) } + // TODO: song loader sort order val songs = songRepository.songs( - songRepository.makeSongCursor( - AudioColumns.ARTIST_ID + "=?", - arrayOf(artistId.toString()), - getSongLoaderSortOrder() - ) + SongQuery(artistId = LongFilter(eq = artistId)) ) return Artist(artistId, albumRepository.splitIntoAlbums(songs)) } @@ -74,68 +68,51 @@ class RealArtistRepository( override fun albumArtist(artistName: String): Artist { if (artistName == Artist.VARIOUS_ARTISTS_DISPLAY_NAME) { // Get Various Artists + // TODO: song loader sort order + // TODO: why is the filtering done AFTER the query here? val songs = songRepository.songs( - songRepository.makeSongCursor( - null, - null, - getSongLoaderSortOrder() - ) + SongQuery() ) val albums = albumRepository.splitIntoAlbums(songs) .filter { it.albumArtist == Artist.VARIOUS_ARTISTS_DISPLAY_NAME } return Artist(Artist.VARIOUS_ARTISTS_ID, albums, true) } + // TODO: song loader sort order val songs = songRepository.songs( - songRepository.makeSongCursor( - "album_artist" + "=?", - arrayOf(artistName), - getSongLoaderSortOrder() - ) + SongQuery(albumArtist = StringFilter(eq = artistName)) ) return Artist(artistName, albumRepository.splitIntoAlbums(songs), true) } override fun artists(): List { + // TODO: song loader sort order val songs = songRepository.songs( - songRepository.makeSongCursor( - null, null, - getSongLoaderSortOrder() - ) + SongQuery() ) return splitIntoArtists(albumRepository.splitIntoAlbums(songs)) } override fun albumArtists(): List { + // TODO: sort "lower($ALBUM_ARTIST)" + if (PreferenceUtil.artistSortOrder == SortOrder.ArtistSortOrder.ARTIST_A_Z) "" else " DESC" val songs = songRepository.songs( - songRepository.makeSongCursor( - null, - null, - "lower($ALBUM_ARTIST)" + - if (PreferenceUtil.artistSortOrder == SortOrder.ArtistSortOrder.ARTIST_A_Z) "" else " DESC" - ) + SongQuery() ) return splitIntoAlbumArtists(albumRepository.splitIntoAlbums(songs)) } override fun albumArtists(query: String): List { + // TODO: song loader sort order val songs = songRepository.songs( - songRepository.makeSongCursor( - "album_artist" + " LIKE ?", - arrayOf("%$query%"), - getSongLoaderSortOrder() - ) + SongQuery(albumArtist = StringFilter(like = "%$query%")) ) return splitIntoAlbumArtists(albumRepository.splitIntoAlbums(songs)) } override fun artists(query: String): List { + // TODO: song loader sort oder val songs = songRepository.songs( - songRepository.makeSongCursor( - AudioColumns.ARTIST + " LIKE ?", - arrayOf("%$query%"), - getSongLoaderSortOrder() - ) + SongQuery(artistName = StringFilter(like = "%$query%")) ) return splitIntoArtists(albumRepository.splitIntoAlbums(songs)) } diff --git a/app/src/main/java/code/name/monkey/retromusic/repository/GenreRepository.kt b/app/src/main/java/code/name/monkey/retromusic/repository/GenreRepository.kt index 2c096092..85a70184 100644 --- a/app/src/main/java/code/name/monkey/retromusic/repository/GenreRepository.kt +++ b/app/src/main/java/code/name/monkey/retromusic/repository/GenreRepository.kt @@ -16,7 +16,6 @@ package code.name.monkey.retromusic.repository import android.content.ContentResolver import android.database.Cursor -import android.net.Uri import android.provider.BaseColumns import android.provider.MediaStore.Audio.Genres import code.name.monkey.retromusic.Constants.IS_MUSIC @@ -36,6 +35,7 @@ interface GenreRepository { fun song(genreId: Long): Song } +// TODO: move genre info into song model class RealGenreRepository( private val contentResolver: ContentResolver, private val songRepository: RealSongRepository @@ -50,11 +50,11 @@ class RealGenreRepository( // so we need to get songs without a genre a different way. return if (genreId == -1L) { getSongsWithNoGenre() - } else songRepository.songs(makeGenreSongCursor(genreId)) + } else songRepository.songs() } override fun song(genreId: Long): Song { - return songRepository.song(makeGenreSongCursor(genreId)) + return songRepository.song(SongQuery()) } private fun getGenreFromCursor(cursor: Cursor): Genre { @@ -72,31 +72,10 @@ class RealGenreRepository( } private fun getSongsWithNoGenre(): List { + // TODO: do this somehow (probably by actually storing genres on songs in our model) val selection = BaseColumns._ID + " NOT IN " + "(SELECT " + Genres.Members.AUDIO_ID + " FROM audio_genres_map)" - return songRepository.songs(songRepository.makeSongCursor(selection, null)) - } - - private fun hasSongsWithNoGenre(): Boolean { - val allSongsCursor = songRepository.makeSongCursor(null, null) - val allSongsWithGenreCursor = makeAllSongsWithGenreCursor() - - if (allSongsCursor == null || allSongsWithGenreCursor == null) { - return false - } - - val hasSongsWithNoGenre = allSongsCursor.count > allSongsWithGenreCursor.count - allSongsCursor.close() - allSongsWithGenreCursor.close() - return hasSongsWithNoGenre - } - - private fun makeAllSongsWithGenreCursor(): Cursor? { - println(Genres.EXTERNAL_CONTENT_URI.toString()) - return contentResolver.query( - Uri.parse("content://media/external/audio/genres/all/members"), - arrayOf(Genres.Members.AUDIO_ID), null, null, null - ) + return songRepository.songs() } private fun makeGenreSongCursor(genreId: Long): Cursor? { diff --git a/app/src/main/java/code/name/monkey/retromusic/repository/LastAddedSongsRepository.kt b/app/src/main/java/code/name/monkey/retromusic/repository/LastAddedSongsRepository.kt index 153eb375..8c99f97d 100644 --- a/app/src/main/java/code/name/monkey/retromusic/repository/LastAddedSongsRepository.kt +++ b/app/src/main/java/code/name/monkey/retromusic/repository/LastAddedSongsRepository.kt @@ -14,12 +14,11 @@ package code.name.monkey.retromusic.repository -import android.database.Cursor -import android.provider.MediaStore import code.name.monkey.retromusic.model.Album import code.name.monkey.retromusic.model.Artist import code.name.monkey.retromusic.model.Song import code.name.monkey.retromusic.util.PreferenceUtil +import software.lavender.music.query.LongFilter /** * Created by hemanths on 16/08/17. @@ -38,7 +37,8 @@ class RealLastAddedRepository( private val artistRepository: RealArtistRepository ) : LastAddedRepository { override fun recentSongs(): List { - return songRepository.songs(makeLastAddedCursor()) + // TODO: sort by date added, descending + return songRepository.songs(SongQuery(dateModified = LongFilter(gt = PreferenceUtil.lastAddedCutoff))) } override fun recentAlbums(): List { @@ -48,13 +48,4 @@ class RealLastAddedRepository( override fun recentArtists(): List { return artistRepository.splitIntoArtists(recentAlbums()) } - - private fun makeLastAddedCursor(): Cursor? { - val cutoff = PreferenceUtil.lastAddedCutoff - return songRepository.makeSongCursor( - MediaStore.Audio.Media.DATE_ADDED + ">?", - arrayOf(cutoff.toString()), - MediaStore.Audio.Media.DATE_ADDED + " DESC" - ) - } } diff --git a/app/src/main/java/code/name/monkey/retromusic/repository/SongRepository.kt b/app/src/main/java/code/name/monkey/retromusic/repository/SongRepository.kt index 6a932ab2..5ad0eb98 100644 --- a/app/src/main/java/code/name/monkey/retromusic/repository/SongRepository.kt +++ b/app/src/main/java/code/name/monkey/retromusic/repository/SongRepository.kt @@ -15,21 +15,48 @@ package code.name.monkey.retromusic.repository import android.content.Context -import android.database.Cursor -import android.os.Environment -import android.provider.MediaStore -import android.provider.MediaStore.Audio.AudioColumns -import android.provider.MediaStore.Audio.Media -import code.name.monkey.retromusic.Constants.IS_MUSIC -import code.name.monkey.retromusic.Constants.baseProjection -import code.name.monkey.retromusic.extensions.getInt -import code.name.monkey.retromusic.extensions.getLong -import code.name.monkey.retromusic.extensions.getString -import code.name.monkey.retromusic.extensions.getStringOrNull import code.name.monkey.retromusic.model.Song -import code.name.monkey.retromusic.providers.BlacklistStore -import code.name.monkey.retromusic.util.PreferenceUtil -import java.util.* +import software.lavender.music.query.IntFilter +import software.lavender.music.query.LongFilter +import software.lavender.music.query.Query +import software.lavender.music.query.StringFilter + +// TODO: sorting +data class SongQuery( + val id: LongFilter? = null, + val title: StringFilter? = null, + val year: IntFilter? = null, + val data: StringFilter? = null, + val dateModified: LongFilter? = null, + val albumId: LongFilter? = null, + val albumName: StringFilter? = null, + val artistId: LongFilter? = null, + val artistName: StringFilter? = null, + val composer: StringFilter? = null, + val albumArtist: StringFilter? = null +) : Query { + override fun apply(testee: Song): Boolean { + if (id != null && !id.matches(testee.id)) { + return false + } + if (title != null && !title.matches(testee.title)) { + return false + } + if (year != null && !year.matches(testee.year)) { + return false + } + if (data != null && !data.matches(testee.data)) { + return false + } + if (dateModified != null && !dateModified.matches(testee.dateModified)) { + return false + } + if (albumId != null && !albumId.matches(testee.albumId)) { + return false + } + return true + } +} /** * Created by hemanths on 10/08/17. @@ -38,13 +65,13 @@ interface SongRepository { fun songs(): List - fun songs(cursor: Cursor?): List + fun songs(query: SongQuery): List fun songs(query: String): List fun songsByFilePath(filePath: String): List - fun song(cursor: Cursor?): Song + fun song(query: SongQuery): Song fun song(songId: Long): Song } @@ -52,162 +79,54 @@ interface SongRepository { class RealSongRepository(private val context: Context) : SongRepository { override fun songs(): List { - return songs(makeSongCursor(null, null)) + return listOf( + Song( + 1, + "example", + 1, + 2021, + 23723478, + "uuh idk what goes here yet, i think the file path or something", + 1639589623, + 1, + "example", + 1, + "example", + "example", + "example" + ) + ) } - override fun songs(cursor: Cursor?): List { + override fun songs(query: SongQuery): List { val songs = arrayListOf() - if (cursor != null && cursor.moveToFirst()) { - do { - songs.add(getSongFromCursorImpl(cursor)) - } while (cursor.moveToNext()) + for (song in songs()) { + if (query.apply(song)) { + songs.add(song) + } } - cursor?.close() return songs } - override fun song(cursor: Cursor?): Song { - val song: Song = if (cursor != null && cursor.moveToFirst()) { - getSongFromCursorImpl(cursor) - } else { - Song.emptySong + override fun song(query: SongQuery): Song { + val songs = arrayListOf() + for (song in songs()) { + if (query.apply(song)) { + songs.add(song) + } } - cursor?.close() - return song + return songs.firstOrNull() ?: Song.emptySong } override fun songs(query: String): List { - return songs(makeSongCursor(AudioColumns.TITLE + " LIKE ?", arrayOf("%$query%"))) + return songs(SongQuery(title = StringFilter(like = "%$query%"))) } override fun song(songId: Long): Song { - return song(makeSongCursor(AudioColumns._ID + "=?", arrayOf(songId.toString()))) + return song(SongQuery(id = LongFilter(eq = songId))) } override fun songsByFilePath(filePath: String): List { - return songs( - makeSongCursor( - AudioColumns.DATA + "=?", - arrayOf(filePath) - ) - ) - } - - private fun getSongFromCursorImpl( - cursor: Cursor - ): Song { - val id = cursor.getLong(AudioColumns._ID) - val title = cursor.getString(AudioColumns.TITLE) - val trackNumber = cursor.getInt(AudioColumns.TRACK) - val year = cursor.getInt(AudioColumns.YEAR) - val duration = cursor.getLong(AudioColumns.DURATION) - val data = cursor.getString(AudioColumns.DATA) - val dateModified = cursor.getLong(AudioColumns.DATE_MODIFIED) - val albumId = cursor.getLong(AudioColumns.ALBUM_ID) - val albumName = cursor.getStringOrNull(AudioColumns.ALBUM) - val artistId = cursor.getLong(AudioColumns.ARTIST_ID) - val artistName = cursor.getStringOrNull(AudioColumns.ARTIST) - val composer = cursor.getStringOrNull(AudioColumns.COMPOSER) - val albumArtist = cursor.getStringOrNull("album_artist") - return Song( - id, - title, - trackNumber, - year, - duration, - data, - dateModified, - albumId, - albumName ?: "", - artistId, - artistName ?: "", - composer ?: "", - albumArtist ?: "" - ) - } - - @JvmOverloads - fun makeSongCursor( - - selection: String?, - selectionValues: Array?, - sortOrder: String = PreferenceUtil.songSortOrder - ): Cursor? { - var selectionFinal = selection - var selectionValuesFinal = selectionValues - selectionFinal = if (selection != null && selection.trim { it <= ' ' } != "") { - "$IS_MUSIC AND $selectionFinal" - } else { - IS_MUSIC - } - - // Whitelist - if (PreferenceUtil.isWhiteList) { - selectionFinal = - selectionFinal + " AND " + AudioColumns.DATA + " LIKE ?" - selectionValuesFinal = addSelectionValues( - selectionValuesFinal, arrayListOf( - Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MUSIC).canonicalPath - ) - ) - } else { - // Blacklist - val paths = BlacklistStore.getInstance(context).paths - if (paths.isNotEmpty()) { - selectionFinal = generateBlacklistSelection(selectionFinal, paths.size) - selectionValuesFinal = addSelectionValues(selectionValuesFinal, paths) - } - } - - selectionFinal = - selectionFinal + " AND " + Media.DURATION + ">= " + (PreferenceUtil.filterLength * 1000) - - val uri = if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.Q) { - Media.getContentUri(MediaStore.VOLUME_EXTERNAL) - } else { - Media.EXTERNAL_CONTENT_URI - } - return try { - context.contentResolver.query( - uri, - baseProjection, - selectionFinal, - selectionValuesFinal, - sortOrder - ) - } catch (ex: SecurityException) { - return null - } - } - - private fun generateBlacklistSelection( - selection: String?, - pathCount: Int - ): String { - val newSelection = StringBuilder( - if (selection != null && selection.trim { it <= ' ' } != "") "$selection AND " else "") - newSelection.append(AudioColumns.DATA + " NOT LIKE ?") - for (i in 0 until pathCount - 1) { - newSelection.append(" AND " + AudioColumns.DATA + " NOT LIKE ?") - } - return newSelection.toString() - } - - private fun addSelectionValues( - selectionValues: Array?, - paths: ArrayList - ): Array { - var selectionValuesFinal = selectionValues - if (selectionValuesFinal == null) { - selectionValuesFinal = emptyArray() - } - val newSelectionValues = Array(selectionValuesFinal.size + paths.size) { - "n = $it" - } - System.arraycopy(selectionValuesFinal, 0, newSelectionValues, 0, selectionValuesFinal.size) - for (i in selectionValuesFinal.size until newSelectionValues.size) { - newSelectionValues[i] = paths[i - selectionValuesFinal.size] + "%" - } - return newSelectionValues + return songs(SongQuery(data = StringFilter(eq = filePath))) } } diff --git a/app/src/main/java/code/name/monkey/retromusic/repository/TopPlayedRepository.kt b/app/src/main/java/code/name/monkey/retromusic/repository/TopPlayedRepository.kt index 810c401d..fa518ec5 100644 --- a/app/src/main/java/code/name/monkey/retromusic/repository/TopPlayedRepository.kt +++ b/app/src/main/java/code/name/monkey/retromusic/repository/TopPlayedRepository.kt @@ -16,8 +16,6 @@ package code.name.monkey.retromusic.repository import android.content.Context import android.database.Cursor -import android.provider.BaseColumns -import android.provider.MediaStore import code.name.monkey.retromusic.Constants.NUMBER_OF_TOP_TRACKS import code.name.monkey.retromusic.model.Album import code.name.monkey.retromusic.model.Artist @@ -51,29 +49,27 @@ class RealTopPlayedRepository( ) : TopPlayedRepository { override fun recentlyPlayedTracks(): List { - return songRepository.songs(makeRecentTracksCursorAndClearUpDatabase()) + // TODO: + return songRepository.songs() } override fun topTracks(): List { - return songRepository.songs(makeTopTracksCursorAndClearUpDatabase()) + // TODO: + return songRepository.songs() } override fun notRecentlyPlayedTracks(): List { val allSongs = mutableListOf().apply { addAll( - songRepository.songs( - songRepository.makeSongCursor( - null, null, - MediaStore.Audio.Media.DATE_ADDED + " ASC" - ) - ) + // TODO: sort by date added asc + songRepository.songs() ) } val playedSongs = songRepository.songs( - makePlayedTracksCursorAndClearUpDatabase() + // TODO: makePlayedTracksCursorAndClearUpDatabase() ) val notRecentlyPlayedSongs = songRepository.songs( - makeNotRecentTracksCursorAndClearUpDatabase() + // TODO: makeNotRecentTracksCursorAndClearUpDatabase() ) allSongs.removeAll(playedSongs.toSet()) allSongs.addAll(notRecentlyPlayedSongs) @@ -131,7 +127,7 @@ class RealTopPlayedRepository( cursor: Cursor?, idColumn: Int ): SortedLongCursor? { - if (cursor != null && cursor.moveToFirst()) { + /*if (cursor != null && cursor.moveToFirst()) { // create the list of ids to select against val selection = StringBuilder() selection.append(BaseColumns._ID) @@ -164,7 +160,7 @@ class RealTopPlayedRepository( BaseColumns._ID ) } - } + } */ return null } diff --git a/app/src/main/java/code/name/monkey/retromusic/util/FileUtil.java b/app/src/main/java/code/name/monkey/retromusic/util/FileUtil.java index 78b77898..191ec03c 100644 --- a/app/src/main/java/code/name/monkey/retromusic/util/FileUtil.java +++ b/app/src/main/java/code/name/monkey/retromusic/util/FileUtil.java @@ -15,35 +15,18 @@ package code.name.monkey.retromusic.util; import android.content.Context; -import android.database.Cursor; import android.os.Environment; -import android.provider.MediaStore; import android.webkit.MimeTypeMap; - import androidx.annotation.NonNull; import androidx.annotation.Nullable; - -import java.io.BufferedReader; -import java.io.ByteArrayOutputStream; -import java.io.File; -import java.io.FileFilter; -import java.io.FileInputStream; -import java.io.FileReader; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.HashSet; -import java.util.LinkedList; -import java.util.List; -import java.util.StringTokenizer; - import code.name.monkey.retromusic.adapter.Storage; import code.name.monkey.retromusic.model.Song; import code.name.monkey.retromusic.repository.RealSongRepository; -import code.name.monkey.retromusic.repository.SortedCursor; +import code.name.monkey.retromusic.repository.SongQuery; +import software.lavender.music.query.StringFilter; + +import java.io.*; +import java.util.*; public final class FileUtil { @@ -63,53 +46,31 @@ public final class FileUtil { @NonNull public static List matchFilesWithMediaStore( @NonNull Context context, @Nullable List files) { - return new RealSongRepository(context).songs(makeSongCursor(context, files)); + return new RealSongRepository(context).songs(makeSongQuery(files)); } - public static String safeGetCanonicalPath(File file) { - try { - return file.getCanonicalPath(); - } catch (IOException e) { - e.printStackTrace(); - return file.getAbsolutePath(); - } - } - - @Nullable - public static SortedCursor makeSongCursor( - @NonNull final Context context, @Nullable final List files) { - String selection = null; - String[] paths = null; - - if (files != null) { - paths = toPathArray(files); - - if (files.size() > 0 - && files.size() < 999) { // 999 is the max amount Androids SQL implementation can handle. - selection = - MediaStore.Audio.AudioColumns.DATA + " IN (" + makePlaceholders(files.size()) + ")"; - } + public static String safeGetCanonicalPath(File file) { + try { + return file.getCanonicalPath(); + } catch (IOException e) { + e.printStackTrace(); + return file.getAbsolutePath(); + } } - Cursor songCursor = - new RealSongRepository(context).makeSongCursor(selection, selection == null ? null : paths); + @Nullable + public static SongQuery makeSongQuery(@Nullable final List files) { + String[] paths = null; - return songCursor == null - ? null - : new SortedCursor(songCursor, paths, MediaStore.Audio.AudioColumns.DATA); - } + if (files != null) { + paths = toPathArray(files); + } - private static String makePlaceholders(int len) { - StringBuilder sb = new StringBuilder(len * 2 - 1); - sb.append("?"); - for (int i = 1; i < len; i++) { - sb.append(",?"); + return new SongQuery(null, null, null, new StringFilter(null, null, null, paths, false, false), null, null, null, null, null, null, null); } - return sb.toString(); - } - @Nullable - private static String[] toPathArray(@Nullable List files) { + @Nullable + private static String[] toPathArray(@Nullable List files) { if (files != null) { String[] paths = new String[files.size()]; for (int i = 0; i < files.size(); i++) { diff --git a/app/src/main/java/software/lavender/music/query/extensions.kt b/app/src/main/java/software/lavender/music/query/extensions.kt new file mode 100644 index 00000000..3657d76f --- /dev/null +++ b/app/src/main/java/software/lavender/music/query/extensions.kt @@ -0,0 +1,14 @@ +package software.lavender.music.query + +fun String.like(matcher: String): Boolean { + if (matcher.startsWith('%')) { + if (matcher.endsWith('%')) { + return contains(matcher.substring(1, matcher.length - 2)) + } + return endsWith(matcher.substring(1)) + } + if (matcher.endsWith('%')) { + return startsWith(matcher.substring(0, matcher.length - 2)) + } + return this == matcher +} \ No newline at end of file diff --git a/app/src/main/java/software/lavender/music/query/filters.kt b/app/src/main/java/software/lavender/music/query/filters.kt new file mode 100644 index 00000000..bca24bc5 --- /dev/null +++ b/app/src/main/java/software/lavender/music/query/filters.kt @@ -0,0 +1,87 @@ +package software.lavender.music.query + +interface QueryFilter { + fun matches(testee: T): Boolean +} + +abstract class NumericFilter( + val gt: T?, + val lt: T?, + val eq: T?, + val ne: T? +) : QueryFilter + +class IntFilter( + gt: Int? = null, + lt: Int? = null, + eq: Int? = null, + ne: Int? = null +) : NumericFilter(gt, lt, eq, ne) { + override fun matches(testee: Int): Boolean { + if (eq != null) { + return testee == eq + } + if (lt != null && testee >= lt) { + return false + } + if (gt != null && testee <= gt) { + return false + } + if (ne != null && testee == ne) { + return false + } + return true + } +} + +class LongFilter( + gt: Long? = null, + lt: Long? = null, + eq: Long? = null, + ne: Long? = null +) : NumericFilter(gt, lt, eq, ne) { + override fun matches(testee: Long): Boolean { + if (eq != null) { + return testee == eq + } + if (lt != null && testee >= lt) { + return false + } + if (gt != null && testee <= gt) { + return false + } + if (ne != null && testee == ne) { + return false + } + return true + } +} + +class StringFilter( + val eq: String? = null, + val like: String? = null, + val ne: String? = null, + val _in: Array? = null, + val isNull: Boolean = false, + val lower: Boolean = false, +) : QueryFilter { + override fun matches(testee: String?): Boolean { + if (testee == null) { + return isNull + } + val other = if (lower) testee.lowercase() else testee + if (eq != null) { + return other == eq + } + if (like != null) { + return other.like(like) + } + if (ne != null) { + return other != ne + } + if (_in != null) { + return _in.contains(other) + } + return true + } +} \ No newline at end of file diff --git a/app/src/main/java/software/lavender/music/query/query.kt b/app/src/main/java/software/lavender/music/query/query.kt new file mode 100644 index 00000000..cd3e1ff3 --- /dev/null +++ b/app/src/main/java/software/lavender/music/query/query.kt @@ -0,0 +1,5 @@ +package software.lavender.music.query + +interface Query { + fun apply(testee: T): Boolean +} \ No newline at end of file