/** * Builds a query that will return all the songs represented by the given parameters. * * @param type MediaUtils.TYPE_ARTIST, TYPE_ALBUM, or TYPE_SONG. * @param id The MediaStore id of the song, artist, or album. * @param projection The columns to query. * @param select An extra selection to pass to the query, or null. * @return The initialized query. */ private static QueryTask buildMediaQuery(int type, long id, String[] projection, String select) { Uri media = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI; StringBuilder selection = new StringBuilder(); String sort = DEFAULT_SORT; switch (type) { case TYPE_SONG: selection.append(MediaStore.Audio.Media._ID); break; case TYPE_ARTIST: selection.append(MediaStore.Audio.Media.ARTIST_ID); break; case TYPE_ALBUM: selection.append(MediaStore.Audio.Media.ALBUM_ID); sort = ALBUM_SORT; break; default: throw new IllegalArgumentException("Invalid type specified: " + type); } selection.append('='); selection.append(id); selection.append(" AND length(_data) AND " + MediaStore.Audio.Media.IS_MUSIC); if (select != null) { selection.append(" AND "); selection.append(select); } QueryTask result = new QueryTask(media, projection, selection.toString(), null, sort); result.type = type; return result; }
/** * Builds a query that will return all the songs in the playlist with the given id. * * @param id The id of the playlist in MediaStore.Audio.Playlists. * @param projection The columns to query. * @param selection The selection to pass to the query, or null. * @return The initialized query. */ public static QueryTask buildPlaylistQuery(long id, String[] projection, String selection) { Uri uri = MediaStore.Audio.Playlists.Members.getContentUri("external", id); String sort = MediaStore.Audio.Playlists.Members.PLAY_ORDER; QueryTask result = new QueryTask(uri, projection, selection, null, sort); result.type = TYPE_PLAYLIST; return result; }
/** * Build a query that will contain all the media under the given path. * * @param path The path, e.g. /mnt/sdcard/music/ * @param projection The columns to query * @return The initialized query. */ public static QueryTask buildFileQuery(String path, String[] projection) { /* make sure that the path is: -> fixed-up to point to the real mountpoint if user browsed to the mediadir symlink -> terminated with a / if it is a directory -> ended with a % for the LIKE query */ path = addDirEndSlash(sanitizeMediaPath(path)) + "%"; final String query = "_data LIKE ? AND " + MediaStore.Audio.Media.IS_MUSIC; String[] qargs = {path}; Uri media = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI; QueryTask result = new QueryTask(media, projection, query, qargs, DEFAULT_SORT); result.type = TYPE_FILE; return result; }
/** * Builds a query that will return all the songs in the genre with the given id. * * @param id The id of the genre in MediaStore.Audio.Genres. * @param projection The columns to query. * @param selection The selection to pass to the query, or null. * @param selectionArgs The arguments to substitute into the selection. * @param sort The sort order. * @param type The media type to query and return * @param returnSongs returns matching songs instead of `type' if true */ public static QueryTask buildGenreQuery( long id, String[] projection, String selection, String[] selectionArgs, String sort, int type, boolean returnSongs) { // Note: This function works on a raw sql query with way too much internal // knowledge about the mediaProvider SQL table layout. Yes: it's ugly. // The reason for this mess is that android has a very crippled genre implementation // and does, for example, not allow us to query the albumbs beloging to a genre. Uri uri = MediaStore.Audio.Genres.Members.getContentUri("external", id); String[] clonedProjection = projection .clone(); // we modify the projection, but this should not be visible to the caller String sql = ""; String authority = "audio"; if (type == TYPE_ARTIST) authority = "artist_info"; if (type == TYPE_ALBUM) authority = "album_info"; // Our raw SQL query includes the album_info table (well: it's actually a view) // which shares some columns with audio. // This regexp should matche duplicate column names and forces them to use // the audio table as a source final String _FORCE_AUDIO_SRC = "(^|[ |,\\(])(_id|album(_\\w+)?|artist(_\\w+)?)"; // Prefix the SELECTed rows with the current table authority name for (int i = 0; i < clonedProjection.length; i++) { if (clonedProjection[i].equals("0") == false) // do not prefix fake rows clonedProjection[i] = (returnSongs ? "audio" : authority) + "." + clonedProjection[i]; } sql += TextUtils.join(", ", clonedProjection); sql += " FROM audio_genres_map_noid, audio" + (authority.equals("audio") ? "" : ", " + authority); sql += " WHERE(audio._id = audio_id AND genre_id=?)"; if (selection != null && selection.length() > 0) sql += " AND(" + selection.replaceAll(_FORCE_AUDIO_SRC, "$1audio.$2") + ")"; if (type == TYPE_ARTIST) sql += " AND(artist_info._id = audio.artist_id)" + (returnSongs ? "" : " GROUP BY artist_info._id"); if (type == TYPE_ALBUM) sql += " AND(album_info._id = audio.album_id)" + (returnSongs ? "" : " GROUP BY album_info._id"); if (sort != null && sort.length() > 0) sql += " ORDER BY " + sort.replaceAll(_FORCE_AUDIO_SRC, "$1audio.$2"); // We are now turning this into an sql injection. Fun times. clonedProjection[0] = sql + " --"; QueryTask result = new QueryTask(uri, clonedProjection, selection, selectionArgs, sort); result.type = TYPE_GENRE; return result; }