OSDN Git Service

Eleven: Fix artist header transparency
[android-x86/packages-apps-Eleven.git] / src / com / cyngn / eleven / utils / MusicUtils.java
index 5a0e6a4..f2cc778 100644 (file)
@@ -22,7 +22,6 @@ import android.content.Intent;
 import android.content.ServiceConnection;
 import android.database.Cursor;
 import android.net.Uri;
-import android.os.AsyncTask;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.provider.BaseColumns;
@@ -36,22 +35,25 @@ import android.provider.MediaStore.MediaColumns;
 import android.provider.Settings;
 import android.util.Log;
 import android.view.Menu;
-import android.view.SubMenu;
 
+import com.cyngn.eleven.Config;
+import com.cyngn.eleven.Config.SmartPlaylistType;
 import com.cyngn.eleven.IElevenService;
 import com.cyngn.eleven.MusicPlaybackService;
 import com.cyngn.eleven.R;
 import com.cyngn.eleven.loaders.LastAddedLoader;
 import com.cyngn.eleven.loaders.PlaylistLoader;
+import com.cyngn.eleven.loaders.PlaylistSongLoader;
 import com.cyngn.eleven.loaders.SongLoader;
+import com.cyngn.eleven.loaders.TopTracksLoader;
 import com.cyngn.eleven.menu.FragmentMenuItems;
+import com.cyngn.eleven.model.AlbumArtistDetails;
 import com.cyngn.eleven.provider.RecentStore;
+import com.cyngn.eleven.provider.SongPlayCount;
 import com.devspark.appmsg.AppMsg;
 
 import java.io.File;
 import java.util.Arrays;
-import java.util.Formatter;
-import java.util.Locale;
 import java.util.WeakHashMap;
 
 /**
@@ -189,13 +191,13 @@ public final class MusicUtils {
      * @param secs The track in seconds.
      * @return Duration of a track that's properly formatted.
      */
-    public static final String makeTimeString(final Context context, long secs) {
+    public static final String makeShortTimeString(final Context context, long secs) {
         long hours, mins;
 
         hours = secs / 3600;
-        secs -= hours * 3600;
+        secs %= 3600;
         mins = secs / 60;
-        secs -= mins * 60;
+        secs %= 60;
 
         final String durationFormat = context.getResources().getString(
                 hours == 0 ? R.string.durationformatshort : R.string.durationformatlong);
@@ -203,6 +205,48 @@ public final class MusicUtils {
     }
 
     /**
+     * * Used to create a formatted time string in the format of #d #h #m #s
+     *
+     * @param context The {@link Context} to use.
+     * @param secs The duration seconds.
+     * @return Duration properly formatted in #d #h #m #s format
+     */
+    public static final String makeLongTimeString(final Context context, long secs) {
+        long days, hours, mins;
+
+        days = secs / (3600 * 24);
+        secs %= (3600 * 24);
+        hours = secs / 3600;
+        secs %= 3600;
+        mins = secs / 60;
+        secs %= 60;
+
+        int stringId = R.string.duration_mins;
+        if (days != 0) {
+            stringId = R.string.duration_days;
+        } else if (hours != 0) {
+            stringId = R.string.duration_hours;
+        }
+
+        final String durationFormat = context.getResources().getString(stringId);
+        return String.format(durationFormat, days, hours, mins, secs);
+    }
+
+    /**
+     * Used to combine two strings with some kind of separator in between
+     *
+     * @param context The {@link Context} to use.
+     * @param first string to combine
+     * @param second string to combine
+     * @return the combined string
+     */
+    public static final String makeCombinedString(final Context context, final String first,
+                                                  final String second) {
+        final String formatter = context.getResources().getString(R.string.combine_two_strings);
+        return String.format(formatter, first, second);
+    }
+
+    /**
      * Changes to the next track
      */
     public static void next() {
@@ -683,7 +727,7 @@ public final class MusicUtils {
      */
     public static void playAll(final Context context, final long[] list, int position,
             final boolean forceShuffle) {
-        if (list.length == 0 || mService == null) {
+        if (list == null || list.length == 0 || mService == null) {
             return;
         }
         try {
@@ -727,7 +771,7 @@ public final class MusicUtils {
      * @param context The {@link Context} to use.
      */
     public static void shuffleAll(final Context context) {
-        Cursor cursor = SongLoader.makeSongCursor(context);
+        Cursor cursor = SongLoader.makeSongCursor(context, null);
         final long[] mTrackList = getSongListForCursor(cursor);
         final int position = 0;
         if (mTrackList.length == 0 || mService == null) {
@@ -788,10 +832,10 @@ public final class MusicUtils {
      */
     public static final long getIdForArtist(final Context context, final String name) {
         Cursor cursor = context.getContentResolver().query(
-                MediaStore.Audio.Artists.EXTERNAL_CONTENT_URI, new String[] {
-                    BaseColumns._ID
-                }, ArtistColumns.ARTIST + "=?", new String[] {
-                    name
+                MediaStore.Audio.Artists.EXTERNAL_CONTENT_URI, new String[]{
+                        BaseColumns._ID
+                }, ArtistColumns.ARTIST + "=?", new String[]{
+                        name
                 }, ArtistColumns.ARTIST);
         int id = -1;
         if (cursor != null) {
@@ -930,6 +974,7 @@ public final class MusicUtils {
         final String message = context.getResources().getQuantityString(
                 R.plurals.NNNtrackstoplaylist, numinserted, numinserted);
         AppMsg.makeText((Activity)context, message, AppMsg.STYLE_CONFIRM).show();
+        playlistChanged();
     }
 
     /**
@@ -948,6 +993,7 @@ public final class MusicUtils {
         final String message = context.getResources().getQuantityString(
                 R.plurals.NNNtracksfromplaylist, 1, 1);
         AppMsg.makeText((Activity)context, message, AppMsg.STYLE_CONFIRM).show();
+        playlistChanged();
     }
 
     /**
@@ -1005,12 +1051,17 @@ public final class MusicUtils {
         }
     }
 
+    public static final String getSongCountForAlbum(final Context context, final long id) {
+        Integer i = getSongCountForAlbumInt(context, id);
+        return i == null ? null : Integer.toString(i);
+    }
+
     /**
      * @param context The {@link Context} to use.
      * @param id The id of the album.
      * @return The song count for an album.
      */
-    public static final String getSongCountForAlbum(final Context context, final long id) {
+    public static final Integer getSongCountForAlbumInt(final Context context, final long id) {
         if (id == -1) {
             return null;
         }
@@ -1018,11 +1069,13 @@ public final class MusicUtils {
         Cursor cursor = context.getContentResolver().query(uri, new String[] {
                     AlbumColumns.NUMBER_OF_SONGS
                 }, null, null, null);
-        String songCount = null;
+        Integer songCount = null;
         if (cursor != null) {
             cursor.moveToFirst();
             if (!cursor.isAfterLast()) {
-                songCount = cursor.getString(0);
+                if(!cursor.isNull(0)) {
+                    songCount = cursor.getInt(0);
+                }
             }
             cursor.close();
             cursor = null;
@@ -1031,6 +1084,59 @@ public final class MusicUtils {
     }
 
     /**
+     * Gets the number of songs for a playlist
+     * @param context The {@link Context} to use.
+     * @param playlistId the id of the playlist
+     * @return the # of songs in the playlist
+     */
+    public static final int getSongCountForPlaylist(final Context context, final long playlistId) {
+        Cursor c = context.getContentResolver().query(
+                MediaStore.Audio.Playlists.Members.getContentUri("external", playlistId),
+                new String[]{BaseColumns._ID}, MusicUtils.MUSIC_ONLY_SELECTION, null, null);
+
+        if (c != null && c.moveToFirst()) {
+            int count = c.getCount();
+            c.close();
+            c = null;
+            return count;
+        }
+
+        return 0;
+    }
+
+    public static final AlbumArtistDetails getAlbumArtDetails(final Context context, final long trackId) {
+        final StringBuilder selection = new StringBuilder();
+        selection.append(MediaStore.Audio.AudioColumns.IS_MUSIC + "=1");
+        selection.append(" AND " + BaseColumns._ID + " = '" + trackId + "'");
+
+        Cursor cursor = context.getContentResolver().query(
+            MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
+            new String[] {
+                    /* 0 */
+                MediaStore.Audio.AudioColumns.ALBUM_ID,
+                    /* 1 */
+                MediaStore.Audio.AudioColumns.ALBUM,
+                    /* 2 */
+                MediaStore.Audio.AlbumColumns.ARTIST,
+            }, selection.toString(), null, null
+        );
+
+        if (!cursor.moveToFirst()) {
+            cursor.close();
+            return null;
+        }
+
+        AlbumArtistDetails result = new AlbumArtistDetails();
+        result.mAudioId = trackId;
+        result.mAlbumId = cursor.getLong(0);
+        result.mAlbumName = cursor.getString(1);
+        result.mArtistName = cursor.getString(2);
+        cursor.close();
+
+        return result;
+    }
+
+    /**
      * @param context The {@link Context} to use.
      * @param id The id of the album.
      * @return The release date for an album.
@@ -1088,13 +1194,7 @@ public final class MusicUtils {
      * @return The track list for a playlist
      */
     public static final long[] getSongListForPlaylist(final Context context, final long playlistId) {
-        final String[] projection = new String[] {
-            MediaStore.Audio.Playlists.Members.AUDIO_ID
-        };
-        Cursor cursor = context.getContentResolver().query(
-                MediaStore.Audio.Playlists.Members.getContentUri("external",
-                        Long.valueOf(playlistId)), projection, null, null,
-                MediaStore.Audio.Playlists.Members.DEFAULT_SORT_ORDER);
+        Cursor cursor = PlaylistSongLoader.makePlaylistSongCursor(context, playlistId);
 
         if (cursor != null) {
             final long[] list = getSongListForCursor(cursor);
@@ -1120,29 +1220,43 @@ public final class MusicUtils {
 
     /**
      * @param context The {@link Context} to use
+     * @param type The Smart Playlist Type
      * @return The song list for the last added playlist
      */
-    public static final long[] getSongListForLastAdded(final Context context) {
-        final Cursor cursor = LastAddedLoader.makeLastAddedCursor(context);
-        if (cursor != null) {
-            final int count = cursor.getCount();
-            final long[] list = new long[count];
-            for (int i = 0; i < count; i++) {
-                cursor.moveToNext();
-                list[i] = cursor.getLong(0);
+    public static final long[] getSongListForSmartPlaylist(final Context context,
+                                                           final SmartPlaylistType type) {
+        Cursor cursor = null;
+        try {
+            switch (type) {
+                case LastAdded:
+                    cursor = LastAddedLoader.makeLastAddedCursor(context);
+                    break;
+                case RecentlyPlayed:
+                    cursor = TopTracksLoader.makeRecentTracksCursor(context);
+                    break;
+                case TopTracks:
+                    cursor = TopTracksLoader.makeTopTracksCursor(context);
+                    break;
+            }
+            return MusicUtils.getSongListForCursor(cursor);
+        } finally {
+            if (cursor != null) {
+                cursor.close();
+                cursor = null;
             }
-            return list;
         }
-        return sEmptyList;
     }
 
     /**
-     * Plays the last added songs from the past two weeks.
-     *
+     * Plays the smart playlist
      * @param context The {@link Context} to use
+     * @param position the position to start playing from
+     * @param type The Smart Playlist Type
      */
-    public static void playLastAdded(final Context context) {
-        playAll(context, getSongListForLastAdded(context), 0, false);
+    public static void playSmartPlaylist(final Context context, final int position,
+                                         final SmartPlaylistType type) {
+        final long[] list = getSongListForSmartPlaylist(context, type);
+        MusicUtils.playAll(context, list, position, false);
     }
 
     /**
@@ -1189,14 +1303,15 @@ public final class MusicUtils {
     }
 
     /**
-     * Queries {@link RecentStore} for the last album played by an artist
-     *
-     * @param context The {@link Context} to use
-     * @param artistName The artist name
-     * @return The last album name played by an artist
+     * Called when one of playlists have changed
      */
-    public static final String getLastAlbumForArtist(final Context context, final String artistName) {
-        return RecentStore.getInstance(context).getAlbumName(artistName);
+    public static void playlistChanged() {
+        try {
+            if (mService != null) {
+                mService.playlistChanged();
+            }
+        } catch (final RemoteException ignored) {
+        }
     }
 
     /**
@@ -1221,6 +1336,11 @@ public final class MusicUtils {
             try {
                 return mService.position();
             } catch (final RemoteException ignored) {
+            } catch (final IllegalStateException ex) {
+                // Illegal State Exception message is empty so logging will actually throw an
+                // exception.  We should come back and figure out why we get an exception in the
+                // first place and make sure we understand it completely.  I will use
+                // https://cyanogen.atlassian.net/browse/MUSIC-125 to track investigating this more
             }
         }
         return 0;
@@ -1234,6 +1354,11 @@ public final class MusicUtils {
             try {
                 return mService.duration();
             } catch (final RemoteException ignored) {
+            } catch (final IllegalStateException ignored) {
+                // Illegal State Exception message is empty so logging will actually throw an
+                // exception.  We should come back and figure out why we get an exception in the
+                // first place and make sure we understand it completely.  I will use
+                // https://cyanogen.atlassian.net/browse/MUSIC-125 to track investigating this more
             }
         }
         return 0;
@@ -1313,8 +1438,10 @@ public final class MusicUtils {
                 // Remove from current playlist
                 final long id = c.getLong(0);
                 removeTrack(id);
+                // Remove the track from the play count
+                SongPlayCount.getInstance(context).removeItem(id);
                 // Remove any items in the recents database
-                RecentStore.getInstance(context).removeItem(c.getLong(2));
+                RecentStore.getInstance(context).removeItem(id);
                 c.moveToNext();
             }
 
@@ -1364,6 +1491,38 @@ public final class MusicUtils {
     /**
      * A snippet is taken from MediaStore.Audio.keyFor method
      * This will take a name, removes things like "the", "an", etc
+     * as well as special characters and return it
+     * @param name the string to trim
+     * @return the trimmed name
+     */
+    public static String getTrimmedName(String name) {
+        if (name == null || name.length() == 0) {
+            return name;
+        }
+
+        name = name.trim().toLowerCase();
+        if (name.startsWith("the ")) {
+            name = name.substring(4);
+        }
+        if (name.startsWith("an ")) {
+            name = name.substring(3);
+        }
+        if (name.startsWith("a ")) {
+            name = name.substring(2);
+        }
+        if (name.endsWith(", the") || name.endsWith(",the") ||
+                name.endsWith(", an") || name.endsWith(",an") ||
+                name.endsWith(", a") || name.endsWith(",a")) {
+            name = name.substring(0, name.lastIndexOf(','));
+        }
+        name = name.replaceAll("[\\[\\]\\(\\)\"'.,?!]", "").trim();
+
+        return name;
+    }
+
+    /**
+     * A snippet is taken from MediaStore.Audio.keyFor method
+     * This will take a name, removes things like "the", "an", etc
      * as well as special characters, then find the localized label
      * @param name Name to get the label of
      * @param trimName boolean flag to run the trimmer on the name
@@ -1375,22 +1534,7 @@ public final class MusicUtils {
         }
 
         if (trimName) {
-            name = name.trim().toLowerCase();
-            if (name.startsWith("the ")) {
-                name = name.substring(4);
-            }
-            if (name.startsWith("an ")) {
-                name = name.substring(3);
-            }
-            if (name.startsWith("a ")) {
-                name = name.substring(2);
-            }
-            if (name.endsWith(", the") || name.endsWith(",the") ||
-                    name.endsWith(", an") || name.endsWith(",an") ||
-                    name.endsWith(", a") || name.endsWith(",a")) {
-                name = name.substring(0, name.lastIndexOf(','));
-            }
-            name = name.replaceAll("[\\[\\]\\(\\)\"'.,?!]", "").trim();
+            name = getTrimmedName(name);
         }
 
         if (name.length() > 0) {
@@ -1413,4 +1557,15 @@ public final class MusicUtils {
 
         return null;
     }
+
+    /** @return true if a string is null, empty, or contains only whitespace */
+    public static boolean isBlank(String s) {
+        if(s == null) { return true; }
+        if(s.isEmpty()) { return true; }
+        for(int i = 0; i < s.length(); i++) {
+            char c = s.charAt(i);
+            if(!Character.isWhitespace(c)) { return false; }
+        }
+        return true;
+    }
 }