summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorlinus_lee <llee@cyngn.com>2014-09-19 15:27:31 -0700
committerlinus_lee <llee@cyngn.com>2014-11-20 12:03:03 -0800
commite45f89f9521fb203474d85b8ee0675a6fbd4a91c (patch)
treef76f27396de8a56c91611ca4454aef1b1c5cbc0f
parentd5d50247c7fd1389b6cc42ec2d9afe48ed09fdb3 (diff)
downloadandroid_packages_apps_Eleven-e45f89f9521fb203474d85b8ee0675a6fbd4a91c.tar.gz
android_packages_apps_Eleven-e45f89f9521fb203474d85b8ee0675a6fbd4a91c.tar.bz2
android_packages_apps_Eleven-e45f89f9521fb203474d85b8ee0675a6fbd4a91c.zip
Eleven: Move recents logic to song level instead of album level and enable it
This also fixes an empty state bug for playlists not showing the proper text Also adds logic where if ids are not found, they are removed from the SongPlayCount and Recents db https://cyanogen.atlassian.net/browse/MUSIC-34 Change-Id: I550f0481944a6c2bc82c0270a4f8024c7339d86e
-rw-r--r--res/values/strings.xml6
-rw-r--r--src/com/cyngn/eleven/Config.java3
-rw-r--r--src/com/cyngn/eleven/MusicPlaybackService.java4
-rw-r--r--src/com/cyngn/eleven/adapters/PlaylistAdapter.java3
-rw-r--r--src/com/cyngn/eleven/loaders/PlaylistLoader.java5
-rw-r--r--src/com/cyngn/eleven/loaders/RecentLoader.java115
-rw-r--r--src/com/cyngn/eleven/loaders/SortedCursor.java20
-rw-r--r--src/com/cyngn/eleven/loaders/TopTracksLoader.java110
-rw-r--r--src/com/cyngn/eleven/provider/RecentStore.java182
-rw-r--r--src/com/cyngn/eleven/ui/activities/PlaylistDetailActivity.java7
-rw-r--r--src/com/cyngn/eleven/ui/activities/SmartPlaylistDetailActivity.java7
-rw-r--r--src/com/cyngn/eleven/ui/fragments/RecentFragment.java288
-rw-r--r--src/com/cyngn/eleven/ui/fragments/profile/BasicSongFragment.java5
-rw-r--r--src/com/cyngn/eleven/ui/fragments/profile/TopTracksFragment.java2
-rw-r--r--src/com/cyngn/eleven/utils/MusicUtils.java15
15 files changed, 257 insertions, 515 deletions
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 120b056..3356d91 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -78,7 +78,8 @@
<!-- playlist_favorite used in voice search processing only -->
<string name="playlist_favorite">Favorite</string>
<string name="playlist_favorites">Favorites</string>
- <string name="playlist_last_added">Last added</string>
+ <string name="playlist_last_added">Last Added</string>
+ <string name="playlist_recently_played">Recently Played</string>
<string name="playlist_top_tracks">My Top Tracks</string>
<!-- AlertDialog items -->
@@ -177,7 +178,8 @@
<string name="empty_search">NO SEARCH RESULTS FOR</string>
<string name="empty_search_check">Please check that you have the correct spelling or try a different keyword.</string>
<string name="empty_favorite">Songs you mark as favorites will be shown here.</string>
- <string name="empty_recent">Albums you have listened to will show up here. Try playing some music.</string>
+ <string name="empty_recent_main">NO RECENT SONGS</string>
+ <string name="empty_recent">Songs you have listened to recently will show up here.</string>
<string name="empty_playlist_main">NO SONGS IN PLAYLIST</string>
<string name="empty_playlist_secondary">To add songs to this playlist, tap the options menu on a song, album or artist and select \"Add to playlist\".</string>
<string name="empty_top_tracks_main">NO TOP TRACKS</string>
diff --git a/src/com/cyngn/eleven/Config.java b/src/com/cyngn/eleven/Config.java
index 99e90fb..8fb973e 100644
--- a/src/com/cyngn/eleven/Config.java
+++ b/src/com/cyngn/eleven/Config.java
@@ -74,7 +74,8 @@ public final class Config {
public static enum SmartPlaylistType {
LastAdded(-1),
- TopTracks(-2);
+ RecentlyPlayed(-2),
+ TopTracks(-3);
public long mId;
diff --git a/src/com/cyngn/eleven/MusicPlaybackService.java b/src/com/cyngn/eleven/MusicPlaybackService.java
index 1db2d25..b1ffb06 100644
--- a/src/com/cyngn/eleven/MusicPlaybackService.java
+++ b/src/com/cyngn/eleven/MusicPlaybackService.java
@@ -1361,9 +1361,7 @@ public class MusicPlaybackService extends Service {
if (what.equals(META_CHANGED)) {
// Add the track to the recently played list.
- mRecentsCache.addAlbumId(getAlbumId(), getAlbumName(), getArtistName(),
- MusicUtils.getSongCountForAlbum(this, getAlbumId()),
- MusicUtils.getReleaseDateForAlbum(this, getAlbumId()));
+ mRecentsCache.addSongId(getAudioId());
mSongPlayCountCache.bumpSongCount(getAudioId());
} else if (what.equals(QUEUE_CHANGED)) {
diff --git a/src/com/cyngn/eleven/adapters/PlaylistAdapter.java b/src/com/cyngn/eleven/adapters/PlaylistAdapter.java
index 6dd742f..9984777 100644
--- a/src/com/cyngn/eleven/adapters/PlaylistAdapter.java
+++ b/src/com/cyngn/eleven/adapters/PlaylistAdapter.java
@@ -101,6 +101,9 @@ public class PlaylistAdapter extends ArrayAdapter<Playlist> {
case LastAdded:
holder.mImage.get().setImageResource(R.drawable.recently_added);
break;
+ case RecentlyPlayed:
+ holder.mImage.get().setImageResource(R.drawable.recent_icon);
+ break;
case TopTracks:
default:
holder.mImage.get().setImageResource(R.drawable.top_tracks_icon);
diff --git a/src/com/cyngn/eleven/loaders/PlaylistLoader.java b/src/com/cyngn/eleven/loaders/PlaylistLoader.java
index 7b22a0a..4edfdd2 100644
--- a/src/com/cyngn/eleven/loaders/PlaylistLoader.java
+++ b/src/com/cyngn/eleven/loaders/PlaylistLoader.java
@@ -99,6 +99,11 @@ public class PlaylistLoader extends WrappedAsyncTaskLoader<List<Playlist>> {
resources.getString(R.string.playlist_last_added), -1);
mPlaylistList.add(lastAdded);
+ /* Recently Played */
+ final Playlist recentlyPlayed = new Playlist(SmartPlaylistType.RecentlyPlayed.mId,
+ resources.getString(R.string.playlist_recently_played), -1);
+ mPlaylistList.add(recentlyPlayed);
+
/* Top Tracks */
final Playlist topTracks = new Playlist(SmartPlaylistType.TopTracks.mId,
resources.getString(R.string.playlist_top_tracks), -1);
diff --git a/src/com/cyngn/eleven/loaders/RecentLoader.java b/src/com/cyngn/eleven/loaders/RecentLoader.java
deleted file mode 100644
index 2f1f921..0000000
--- a/src/com/cyngn/eleven/loaders/RecentLoader.java
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * Copyright (C) 2012 Andrew Neal Licensed under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with the
- * License. You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law
- * or agreed to in writing, software distributed under the License is
- * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the specific language
- * governing permissions and limitations under the License.
- */
-
-package com.cyngn.eleven.loaders;
-
-import android.content.Context;
-import android.database.Cursor;
-
-import com.cyngn.eleven.R;
-import com.cyngn.eleven.model.Album;
-import com.cyngn.eleven.provider.RecentStore;
-import com.cyngn.eleven.provider.RecentStore.RecentStoreColumns;
-import com.cyngn.eleven.utils.Lists;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Used to query {@link RecentStore} and return the last listened to albums.
- *
- * @author Andrew Neal (andrewdneal@gmail.com)
- */
-public class RecentLoader extends WrappedAsyncTaskLoader<List<Album>> {
-
- /**
- * The result
- */
- private final ArrayList<Album> mAlbumsList = Lists.newArrayList();
-
- /**
- * The {@link Cursor} used to run the query.
- */
- private Cursor mCursor;
-
- /**
- * Constructor of <code>RecentLoader</code>
- *
- * @param context The {@link Context} to use
- */
- public RecentLoader(final Context context) {
- super(context);
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public List<Album> loadInBackground() {
- // Create the Cursor
- mCursor = makeRecentCursor(getContext());
- // Gather the data
- if (mCursor != null && mCursor.moveToFirst()) {
- do {
- // Copy the album id
- final long id = mCursor.getLong(mCursor
- .getColumnIndexOrThrow(RecentStoreColumns.ID));
-
- // Copy the album name
- final String albumName = mCursor.getString(mCursor
- .getColumnIndexOrThrow(RecentStoreColumns.ALBUMNAME));
-
- // Copy the artist name
- final String artist = mCursor.getString(mCursor
- .getColumnIndexOrThrow(RecentStoreColumns.ARTISTNAME));
-
- // Copy the number of songs
- final int songCount = mCursor.getInt(mCursor
- .getColumnIndexOrThrow(RecentStoreColumns.ALBUMSONGCOUNT));
-
- // Copy the release year
- final String year = mCursor.getString(mCursor
- .getColumnIndexOrThrow(RecentStoreColumns.ALBUMYEAR));
-
- // Create a new album
- final Album album = new Album(id, albumName, artist, songCount, year);
-
- // Add everything up
- mAlbumsList.add(album);
- } while (mCursor.moveToNext());
- }
- // Close the cursor
- if (mCursor != null) {
- mCursor.close();
- mCursor = null;
- }
- return mAlbumsList;
- }
-
- /**
- * Creates the {@link Cursor} used to run the query.
- *
- * @param context The {@link Context} to use.
- * @return The {@link Cursor} used to run the album query.
- */
- public static final Cursor makeRecentCursor(final Context context) {
- return RecentStore
- .getInstance(context)
- .getReadableDatabase()
- .query(RecentStoreColumns.NAME,
- new String[] {
- RecentStoreColumns.ID + " as id", RecentStoreColumns.ID,
- RecentStoreColumns.ALBUMNAME, RecentStoreColumns.ARTISTNAME,
- RecentStoreColumns.ALBUMSONGCOUNT, RecentStoreColumns.ALBUMYEAR,
- RecentStoreColumns.TIMEPLAYED
- }, null, null, null, null, RecentStoreColumns.TIMEPLAYED + " DESC");
- }
-}
diff --git a/src/com/cyngn/eleven/loaders/SortedCursor.java b/src/com/cyngn/eleven/loaders/SortedCursor.java
index ffd43b4..6c71ad9 100644
--- a/src/com/cyngn/eleven/loaders/SortedCursor.java
+++ b/src/com/cyngn/eleven/loaders/SortedCursor.java
@@ -19,6 +19,8 @@ public class SortedCursor extends AbstractCursor {
private final Cursor mCursor;
// the map of external indices to internal indices
private ArrayList<Integer> mOrderedPositions;
+ // this contains the ids that weren't found in the underlying cursor
+ private ArrayList<Long> mMissingIds;
/**
* @param cursor to wrap
@@ -31,15 +33,18 @@ public class SortedCursor extends AbstractCursor {
}
mCursor = cursor;
- buildCursorPositionMapping(order, columnName);
+ mMissingIds = buildCursorPositionMapping(order, columnName);
}
/**
* This function populates mOrderedPositions with the cursor positions in the order based
* on the order passed in
* @param order the target order of the internal cursor
+ * @return returns the ids that aren't found in the underlying cursor
*/
- private void buildCursorPositionMapping(final long[] order, final String columnName) {
+ private ArrayList<Long> buildCursorPositionMapping(final long[] order, final String columnName) {
+ ArrayList<Long> missingIds = new ArrayList<Long>();
+
mOrderedPositions = new ArrayList<Integer>(mCursor.getCount());
HashMap<Long, Integer> mapCursorPositions = new HashMap<Long, Integer>(mCursor.getCount());
@@ -56,9 +61,20 @@ public class SortedCursor extends AbstractCursor {
for (long id : order) {
if (mapCursorPositions.containsKey(id)) {
mOrderedPositions.add(mapCursorPositions.get(id));
+ } else {
+ missingIds.add(id);
}
}
}
+
+ return missingIds;
+ }
+
+ /**
+ * @return the list of ids that weren't found in the underlying cursor
+ */
+ public ArrayList<Long> getMissingIds() {
+ return mMissingIds;
}
@Override
diff --git a/src/com/cyngn/eleven/loaders/TopTracksLoader.java b/src/com/cyngn/eleven/loaders/TopTracksLoader.java
index 175eec0..90e1c11 100644
--- a/src/com/cyngn/eleven/loaders/TopTracksLoader.java
+++ b/src/com/cyngn/eleven/loaders/TopTracksLoader.java
@@ -8,53 +8,135 @@ import android.content.Context;
import android.database.Cursor;
import android.provider.BaseColumns;
+import com.cyngn.eleven.provider.RecentStore;
import com.cyngn.eleven.provider.SongPlayCount;
import com.cyngn.eleven.provider.SongPlayCount.SongPlayCountColumns;
+import java.util.ArrayList;
+
/**
* Used to query {@link android.provider.MediaStore.Audio.Media.EXTERNAL_CONTENT_URI} and return
- * the top Songs for a user based on number of times listened and time passed
+ * a sorted list of songs based on either the TopTracks or the RecentSongs
*/
public class TopTracksLoader extends SongLoader {
+ // used for the top played results
public static final int NUMBER_OF_SONGS = 99;
- public TopTracksLoader(final Context context) {
+ public enum QueryType {
+ TopTracks,
+ RecentSongs,
+ }
+
+ protected QueryType mQueryType;
+
+ public TopTracksLoader(final Context context, QueryType type) {
super(context);
+
+ mQueryType = type;
}
@Override
protected Cursor getCursor() {
- return makeTopTracksCursor(mContext);
+ SortedCursor retCursor = null;
+ if (mQueryType == QueryType.TopTracks) {
+ retCursor = makeTopTracksCursor(mContext);
+ } else if (mQueryType == QueryType.RecentSongs) {
+ retCursor = makeRecentTracksCursor(mContext);
+ }
+
+ // clean up the databases with any ids not found
+ if (retCursor != null) {
+ ArrayList<Long> missingIds = retCursor.getMissingIds();
+ if (missingIds != null && missingIds.size() > 0) {
+ // for each unfound id, remove it from the database
+ // this codepath should only really be hit if the user removes songs
+ // outside of the Eleven app
+ for (long id : missingIds) {
+ if (mQueryType == QueryType.TopTracks) {
+ SongPlayCount.getInstance(mContext).removeItem(id);
+ } else if (mQueryType == QueryType.RecentSongs) {
+ RecentStore.getInstance(mContext).removeItem(id);
+ }
+ }
+ }
+ }
+
+ return retCursor;
}
- public static final Cursor makeTopTracksCursor(final Context context) {
+ /**
+ * This creates a sorted cursor based on the top played results
+ * @param context Android context
+ * @return sorted cursor
+ */
+ public static final SortedCursor makeTopTracksCursor(final Context context) {
// first get the top results ids from the internal database
Cursor songs = SongPlayCount.getInstance(context).getTopPlayedResults(NUMBER_OF_SONGS);
- if (songs != null && songs.moveToFirst()) {
- final int idColumnIndex = songs.getColumnIndex(SongPlayCountColumns.ID);
+ try {
+ return makeSortedCursor(context, songs,
+ songs.getColumnIndex(SongPlayCountColumns.ID));
+ } finally {
+ if (songs != null) {
+ songs.close();
+ songs = null;
+ }
+ }
+ }
+
+ /**
+ * This creates a sorted cursor based on the recently played tracks
+ * @param context Android context
+ * @return sorted cursor
+ */
+ public static final SortedCursor makeRecentTracksCursor(final Context context) {
+ // first get the top results ids from the internal database
+ Cursor songs = RecentStore.getInstance(context).queryRecentIds(null);
+
+ try {
+ return makeSortedCursor(context, songs,
+ songs.getColumnIndex(SongPlayCountColumns.ID));
+ } finally {
+ if (songs != null) {
+ songs.close();
+ songs = null;
+ }
+ }
+ }
+ /**
+ * This creates a sorted song cursor given a cursor that contains the sort order
+ * @param context Android context
+ * @param cursor This is the cursor used to determine the order of the ids
+ * @param idColumn the id column index of the cursor
+ * @return a Sorted Cursor of songs
+ */
+ public static final SortedCursor makeSortedCursor(final Context context, final Cursor cursor,
+ final int idColumn) {
+ if (cursor != null && cursor.moveToFirst()) {
// create the list of ids to select against
StringBuilder selection = new StringBuilder();
selection.append(BaseColumns._ID);
selection.append(" IN (");
- selection.append(songs.getString(idColumnIndex));
-
// this tracks the order of the ids
- long[] order = new long[songs.getCount()];
+ long[] order = new long[cursor.getCount()];
- do {
+ long id = cursor.getLong(idColumn);
+ selection.append(id);
+ order[cursor.getPosition()] = id;
+
+ while (cursor.moveToNext()) {
selection.append(",");
- long id = songs.getLong(idColumnIndex);
- order[songs.getPosition()] = id;
+ id = cursor.getLong(idColumn);
+ order[cursor.getPosition()] = id;
selection.append(String.valueOf(id));
- } while (songs.moveToNext());
+ }
selection.append(")");
- // get a list of songs with the data given the selection statment
+ // get a list of songs with the data given the selection statement
Cursor songCursor = makeSongCursor(context, selection.toString());
if (songCursor != null) {
// now return the wrapped TopTracksCursor to handle sorting given order
diff --git a/src/com/cyngn/eleven/provider/RecentStore.java b/src/com/cyngn/eleven/provider/RecentStore.java
index 360b3cc..623b79d 100644
--- a/src/com/cyngn/eleven/provider/RecentStore.java
+++ b/src/com/cyngn/eleven/provider/RecentStore.java
@@ -16,28 +16,17 @@ import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
-import android.text.TextUtils;
-
-/**
- * The {@link RecentlyListenedFragment} is used to display a a grid or list of
- * recently listened to albums. In order to populate the this grid or list with
- * the correct data, we keep a cache of the album ID, name, and time it was
- * played to be retrieved later.
- * <p>
- * In {@link ProfileActivity}, when viewing the profile for an artist, the first
- * image the carousel header is the last album the user listened to for that
- * particular artist. That album is retrieved using
- * {@link #getAlbumName(String)}.
- *
- * @author Andrew Neal (andrewdneal@gmail.com)
- */
+
public class RecentStore extends SQLiteOpenHelper {
/* Version constant to increment when the database should be rebuilt */
private static final int VERSION = 1;
+ /* Maximum # of items in the db */
+ private static final int MAX_ITEMS_IN_DB = 500;
+
/* Name of database file */
- public static final String DATABASENAME = "albumhistory.db";
+ public static final String DATABASENAME = "recenthistory.db";
private static RecentStore sInstance = null;
@@ -56,10 +45,7 @@ public class RecentStore extends SQLiteOpenHelper {
@Override
public void onCreate(final SQLiteDatabase db) {
db.execSQL("CREATE TABLE IF NOT EXISTS " + RecentStoreColumns.NAME + " ("
- + RecentStoreColumns.ID + " LONG NOT NULL," + RecentStoreColumns.ALBUMNAME
- + " TEXT NOT NULL," + RecentStoreColumns.ARTISTNAME + " TEXT NOT NULL,"
- + RecentStoreColumns.ALBUMSONGCOUNT + " TEXT NOT NULL,"
- + RecentStoreColumns.ALBUMYEAR + " TEXT," + RecentStoreColumns.TIMEPLAYED
+ + RecentStoreColumns.ID + " LONG NOT NULL," + RecentStoreColumns.TIMEPLAYED
+ " LONG NOT NULL);");
}
@@ -84,116 +70,94 @@ public class RecentStore extends SQLiteOpenHelper {
}
/**
- * Used to store artist IDs in the database.
+ * Used to store song IDs in the database.
*
- * @param albumIDdThe album's ID.
- * @param albumName The album name.
- * @param artistName The artist album name.
- * @param songCount The number of tracks for the album.
- * @param albumYear The year the album was released.
+ * @param songId The song id to store
*/
- public void addAlbumId(final Long albumId, final String albumName, final String artistName,
- final String songCount, final String albumYear) {
- if (albumId == null || albumName == null || artistName == null || songCount == null) {
- return;
- }
-
+ public void addSongId(final long songId) {
final SQLiteDatabase database = getWritableDatabase();
- final ContentValues values = new ContentValues(6);
-
database.beginTransaction();
- values.put(RecentStoreColumns.ID, albumId);
- values.put(RecentStoreColumns.ALBUMNAME, albumName);
- values.put(RecentStoreColumns.ARTISTNAME, artistName);
- values.put(RecentStoreColumns.ALBUMSONGCOUNT, songCount);
- values.put(RecentStoreColumns.ALBUMYEAR, albumYear);
- values.put(RecentStoreColumns.TIMEPLAYED, System.currentTimeMillis());
-
- database.delete(RecentStoreColumns.NAME, RecentStoreColumns.ID + " = ?", new String[] {
- String.valueOf(albumId)
- });
- database.insert(RecentStoreColumns.NAME, null, values);
- database.setTransactionSuccessful();
- database.endTransaction();
-
- }
-
- /**
- * Used to retrieve the most recently listened album for an artist.
- *
- * @param key The key to reference.
- * @return The most recently listened album for an artist.
- */
- public String getAlbumName(final String key) {
- if (TextUtils.isEmpty(key)) {
- return null;
- }
- final SQLiteDatabase database = getReadableDatabase();
- final String[] projection = new String[] {
- RecentStoreColumns.ID, RecentStoreColumns.ALBUMNAME, RecentStoreColumns.ARTISTNAME,
- RecentStoreColumns.TIMEPLAYED
- };
- final String selection = RecentStoreColumns.ARTISTNAME + "=?";
- final String[] having = new String[] {
- key
- };
- Cursor cursor = database.query(RecentStoreColumns.NAME, projection, selection, having,
- null, null, RecentStoreColumns.TIMEPLAYED + " DESC", null);
- if (cursor != null && cursor.moveToFirst()) {
- cursor.moveToFirst();
- final String album = cursor.getString(cursor
- .getColumnIndexOrThrow(RecentStoreColumns.ALBUMNAME));
- cursor.close();
- cursor = null;
- return album;
- }
- if (cursor != null && !cursor.isClosed()) {
- cursor.close();
- cursor = null;
+ try {
+ // see if the most recent item is the same song id, if it is then don't insert
+ Cursor mostRecentItem = null;
+ try {
+ mostRecentItem = queryRecentIds("1");
+ if (mostRecentItem != null && mostRecentItem.moveToFirst()) {
+ if (songId == mostRecentItem.getLong(0)) {
+ return;
+ }
+ }
+ } finally {
+ if (mostRecentItem != null) {
+ mostRecentItem.close();
+ mostRecentItem = null;
+ }
+ }
+
+ // add the entry
+ final ContentValues values = new ContentValues(2);
+ values.put(RecentStoreColumns.ID, songId);
+ values.put(RecentStoreColumns.TIMEPLAYED, System.currentTimeMillis());
+ database.insert(RecentStoreColumns.NAME, null, values);
+
+ // if our db is too large, delete the extra items
+ Cursor oldest = null;
+ try {
+ database.query(RecentStoreColumns.NAME,
+ new String[]{RecentStoreColumns.ID}, null, null, null, null,
+ RecentStoreColumns.TIMEPLAYED + " ASC");
+
+ if (oldest != null && oldest.getCount() > MAX_ITEMS_IN_DB) {
+ oldest.moveToPosition(oldest.getCount() - MAX_ITEMS_IN_DB);
+ long timeOfRecordToKeep = oldest.getLong(0);
+
+ database.delete(RecentStoreColumns.NAME,
+ RecentStoreColumns.TIMEPLAYED + " < ?",
+ new String[] { String.valueOf(timeOfRecordToKeep) });
+
+ }
+ } finally {
+ if (oldest != null) {
+ oldest.close();
+ oldest = null;
+ }
+ }
+ } finally {
+ database.setTransactionSuccessful();
+ database.endTransaction();
}
-
- return null;
}
/**
- * Clear the cache.
+ * @param songId to remove.
*/
- public void deleteDatabase() {
- final SQLiteDatabase database = getReadableDatabase();
- database.delete(RecentStoreColumns.NAME, null, null);
+ public void removeItem(final long songId) {
+ final SQLiteDatabase database = getWritableDatabase();
+ database.delete(RecentStoreColumns.NAME, RecentStoreColumns.ID + " = ?", new String[] {
+ String.valueOf(songId)
+ });
+
}
/**
- * @param item The album Id to remove.
+ * Gets a cursor to the list of recently played content
+ * @param limit # of songs to limit the result to
+ * @return cursor
*/
- public void removeItem(final long albumId) {
+ public Cursor queryRecentIds(final String limit) {
final SQLiteDatabase database = getReadableDatabase();
- database.delete(RecentStoreColumns.NAME, RecentStoreColumns.ID + " = ?", new String[] {
- String.valueOf(albumId)
- });
-
+ return database.query(RecentStoreColumns.NAME,
+ new String[]{RecentStoreColumns.ID}, null, null, null, null,
+ RecentStoreColumns.TIMEPLAYED + " DESC", limit);
}
public interface RecentStoreColumns {
-
/* Table name */
- public static final String NAME = "albumhistory";
+ public static final String NAME = "recenthistory";
/* Album IDs column */
- public static final String ID = "albumid";
-
- /* Album name column */
- public static final String ALBUMNAME = "itemname";
-
- /* Artist name column */
- public static final String ARTISTNAME = "artistname";
-
- /* Album song count column */
- public static final String ALBUMSONGCOUNT = "albumsongcount";
-
- /* Album year column. It's okay for this to be null */
- public static final String ALBUMYEAR = "albumyear";
+ public static final String ID = "songid";
/* Time played column */
public static final String TIMEPLAYED = "timeplayed";
diff --git a/src/com/cyngn/eleven/ui/activities/PlaylistDetailActivity.java b/src/com/cyngn/eleven/ui/activities/PlaylistDetailActivity.java
index 13f3b29..b020fb1 100644
--- a/src/com/cyngn/eleven/ui/activities/PlaylistDetailActivity.java
+++ b/src/com/cyngn/eleven/ui/activities/PlaylistDetailActivity.java
@@ -164,6 +164,13 @@ public class PlaylistDetailActivity extends DetailActivity implements
// Get the position of the selected item
final AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuInfo;
mSelectedPosition = info.position - 1;
+
+ // the header has been long-pressed - for now just return, but later if we want
+ // to long-press support for the header, do that logic here
+ if (mSelectedPosition == -1) {
+ return;
+ }
+
// Creat a new song
mSong = mAdapter.getItem(mSelectedPosition);
mSelectedId = mSong.mSongId;
diff --git a/src/com/cyngn/eleven/ui/activities/SmartPlaylistDetailActivity.java b/src/com/cyngn/eleven/ui/activities/SmartPlaylistDetailActivity.java
index 1f6f1d8..b908e9d 100644
--- a/src/com/cyngn/eleven/ui/activities/SmartPlaylistDetailActivity.java
+++ b/src/com/cyngn/eleven/ui/activities/SmartPlaylistDetailActivity.java
@@ -10,6 +10,7 @@ import android.view.MenuItem;
import com.cyngn.eleven.Config;
import com.cyngn.eleven.R;
+import com.cyngn.eleven.ui.fragments.RecentFragment;
import com.cyngn.eleven.ui.fragments.profile.LastAddedFragment;
import com.cyngn.eleven.ui.fragments.profile.TopTracksFragment;
@@ -29,6 +30,12 @@ public class SmartPlaylistDetailActivity extends SlidingPanelActivity {
setupActionBar(R.string.playlist_last_added);
break;
+ case RecentlyPlayed:
+ getSupportFragmentManager().beginTransaction()
+ .replace(R.id.activity_base_content, new RecentFragment()).commit();
+
+ setupActionBar(R.string.playlist_recently_played);
+ break;
case TopTracks:
getSupportFragmentManager().beginTransaction()
.replace(R.id.activity_base_content, new TopTracksFragment()).commit();
diff --git a/src/com/cyngn/eleven/ui/fragments/RecentFragment.java b/src/com/cyngn/eleven/ui/fragments/RecentFragment.java
index 09ef35e..c200d4b 100644
--- a/src/com/cyngn/eleven/ui/fragments/RecentFragment.java
+++ b/src/com/cyngn/eleven/ui/fragments/RecentFragment.java
@@ -12,56 +12,35 @@
package com.cyngn.eleven.ui.fragments;
import android.app.Activity;
+import android.database.Cursor;
import android.os.Bundle;
-import android.support.v4.app.Fragment;
-import android.support.v4.app.LoaderManager.LoaderCallbacks;
import android.support.v4.content.Loader;
-import android.view.ContextMenu;
-import android.view.ContextMenu.ContextMenuInfo;
-import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
-import android.view.SubMenu;
import android.view.View;
-import android.view.ViewGroup;
-import android.widget.AbsListView;
-import android.widget.AbsListView.OnScrollListener;
-import android.widget.AdapterView;
-import android.widget.AdapterView.AdapterContextMenuInfo;
-import android.widget.AdapterView.OnItemClickListener;
-import android.widget.GridView;
-import android.widget.ListView;
-import android.widget.TextView;
+import android.view.ContextMenu;
+import android.view.ContextMenu.ContextMenuInfo;
-import com.cyngn.eleven.Config;
import com.cyngn.eleven.MusicStateListener;
import com.cyngn.eleven.R;
-import com.cyngn.eleven.adapters.AlbumAdapter;
-import com.cyngn.eleven.cache.ImageFetcher;
-import com.cyngn.eleven.loaders.RecentLoader;
-import com.cyngn.eleven.menu.CreateNewPlaylist;
-import com.cyngn.eleven.menu.DeleteDialog;
+import com.cyngn.eleven.loaders.TopTracksLoader;
import com.cyngn.eleven.menu.FragmentMenuItems;
-import com.cyngn.eleven.model.Album;
+import com.cyngn.eleven.model.Song;
import com.cyngn.eleven.provider.RecentStore;
-import com.cyngn.eleven.recycler.RecycleHolder;
import com.cyngn.eleven.ui.activities.BaseActivity;
-import com.cyngn.eleven.utils.ApolloUtils;
+import com.cyngn.eleven.ui.fragments.profile.BasicSongFragment;
import com.cyngn.eleven.utils.MusicUtils;
-import com.cyngn.eleven.utils.NavUtils;
-import com.cyngn.eleven.utils.PreferenceUtils;
import com.cyngn.eleven.widgets.NoResultsContainer;
import java.util.List;
/**
- * This class is used to display all of the recently listened to albums by the
+ * This class is used to display all of the recently listened to songs by the
* user.
*
* @author Andrew Neal (andrewdneal@gmail.com)
*/
-public class RecentFragment extends Fragment implements LoaderCallbacks<List<Album>>,
- OnScrollListener, OnItemClickListener, MusicStateListener {
+public class RecentFragment extends BasicSongFragment implements MusicStateListener {
/**
* Used to keep context menu items from bleeding into other fragments
@@ -69,46 +48,11 @@ public class RecentFragment extends Fragment implements LoaderCallbacks<List<Alb
private static final int GROUP_ID = 1;
/**
- * Grid view column count. ONE - list, TWO - normal grid, FOUR - landscape
- */
- private static final int ONE = 1, TWO = 2, FOUR = 4;
-
- /**
* LoaderCallbacks identifier
*/
private static final int LOADER = 0;
/**
- * Fragment UI
- */
- private ViewGroup mRootView;
-
- /**
- * The adapter for the grid
- */
- private AlbumAdapter mAdapter;
-
- /**
- * The grid view
- */
- private GridView mGridView;
-
- /**
- * The list view
- */
- private ListView mListView;
-
- /**
- * Album song list
- */
- private long[] mAlbumList;
-
- /**
- * Represents an album
- */
- private Album mAlbum;
-
- /**
* True if the list should execute {@code #restartLoader()}.
*/
private boolean mShouldRefresh = false;
@@ -127,85 +71,13 @@ public class RecentFragment extends Fragment implements LoaderCallbacks<List<Alb
* {@inheritDoc}
*/
@Override
- public void onCreate(final Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- final int layout = R.layout.list_item_normal;
- mAdapter = new AlbumAdapter(getActivity(), layout);
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public View onCreateView(final LayoutInflater inflater, final ViewGroup container,
- final Bundle savedInstanceState) {
- // The View for the fragment's UI
- mRootView = (ViewGroup)inflater.inflate(R.layout.list_base, null);
- initListView();
-
- return mRootView;
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public void onActivityCreated(final Bundle savedInstanceState) {
- super.onActivityCreated(savedInstanceState);
- // Enable the options menu
- setHasOptionsMenu(true);
- // Start the loader
- getLoaderManager().initLoader(LOADER, null, this);
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public void onPause() {
- super.onPause();
- mAdapter.flush();
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
public void onCreateContextMenu(final ContextMenu menu, final View v,
final ContextMenuInfo menuInfo) {
super.onCreateContextMenu(menu, v, menuInfo);
- // Get the position of the selected item
- final AdapterContextMenuInfo info = (AdapterContextMenuInfo)menuInfo;
- // Create a new album
- mAlbum = mAdapter.getItem(info.position);
- // Create a list of the album's songs
- mAlbumList = MusicUtils.getSongListForAlbum(getActivity(), mAlbum.mAlbumId);
-
- // Play the album
- menu.add(GROUP_ID, FragmentMenuItems.PLAY_SELECTION, Menu.NONE,
- getString(R.string.context_menu_play_selection));
-
- // Add the album to the queue
- menu.add(GROUP_ID, FragmentMenuItems.ADD_TO_QUEUE, Menu.NONE,
- getString(R.string.add_to_queue));
-
- // Add the album to a playlist
- final SubMenu subMenu = menu.addSubMenu(GROUP_ID, FragmentMenuItems.ADD_TO_PLAYLIST,
- Menu.NONE, R.string.add_to_playlist);
- MusicUtils.makePlaylistMenu(getActivity(), GROUP_ID, subMenu);
-
- // View more content by the album artist
- menu.add(GROUP_ID, FragmentMenuItems.MORE_BY_ARTIST, Menu.NONE,
- getString(R.string.context_menu_more_by_artist));
-
// Remove the album from the list
menu.add(GROUP_ID, FragmentMenuItems.REMOVE_FROM_RECENT, Menu.NONE,
getString(R.string.context_menu_remove_from_recent));
-
- // Delete the album
- menu.add(GROUP_ID, FragmentMenuItems.DELETE, Menu.NONE,
- getString(R.string.context_menu_delete));
}
/**
@@ -216,35 +88,11 @@ public class RecentFragment extends Fragment implements LoaderCallbacks<List<Alb
// Avoid leaking context menu selections
if (item.getGroupId() == GROUP_ID) {
switch (item.getItemId()) {
- case FragmentMenuItems.PLAY_SELECTION:
- MusicUtils.playAll(getActivity(), mAlbumList, 0, false);
- return true;
- case FragmentMenuItems.ADD_TO_QUEUE:
- MusicUtils.addToQueue(getActivity(), mAlbumList);
- return true;
- case FragmentMenuItems.NEW_PLAYLIST:
- CreateNewPlaylist.getInstance(mAlbumList).show(getFragmentManager(),
- "CreatePlaylist");
- return true;
- case FragmentMenuItems.MORE_BY_ARTIST:
- NavUtils.openArtistProfile(getActivity(), mAlbum.mArtistName);
- return true;
- case FragmentMenuItems.PLAYLIST_SELECTED:
- final long id = item.getIntent().getLongExtra("playlist", 0);
- MusicUtils.addToPlaylist(getActivity(), mAlbumList, id);
- return true;
case FragmentMenuItems.REMOVE_FROM_RECENT:
mShouldRefresh = true;
- RecentStore.getInstance(getActivity()).removeItem(mAlbum.mAlbumId);
+ RecentStore.getInstance(getActivity()).removeItem(mSelectedId);
MusicUtils.refresh();
return true;
- case FragmentMenuItems.DELETE:
- mShouldRefresh = true;
- final String album = mAlbum.mAlbumName;
- DeleteDialog.newInstance(album, mAlbumList,
- ImageFetcher.generateAlbumCacheKey(album, mAlbum.mArtistName))
- .show(getFragmentManager(), "DeleteDialog");
- return true;
default:
break;
}
@@ -256,123 +104,55 @@ public class RecentFragment extends Fragment implements LoaderCallbacks<List<Alb
* {@inheritDoc}
*/
@Override
- public void onScrollStateChanged(final AbsListView view, final int scrollState) {
- // Pause disk cache access to ensure smoother scrolling
- if (scrollState == AbsListView.OnScrollListener.SCROLL_STATE_FLING
- || scrollState == AbsListView.OnScrollListener.SCROLL_STATE_TOUCH_SCROLL) {
- mAdapter.setPauseDiskCache(true);
- } else {
- mAdapter.setPauseDiskCache(false);
- mAdapter.notifyDataSetChanged();
- }
+ public Loader<List<Song>> onCreateLoader(final int id, final Bundle args) {
+ return new TopTracksLoader(getActivity(), TopTracksLoader.QueryType.RecentSongs);
}
/**
* {@inheritDoc}
*/
@Override
- public void onItemClick(final AdapterView<?> parent, final View view, final int position,
- final long id) {
- mAlbum = mAdapter.getItem(position);
- NavUtils.openAlbumProfile(getActivity(), mAlbum.mAlbumName, mAlbum.mArtistName, mAlbum.mAlbumId);
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public Loader<List<Album>> onCreateLoader(final int id, final Bundle args) {
- return new RecentLoader(getActivity());
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public void onLoadFinished(final Loader<List<Album>> loader, final List<Album> data) {
- // Check for any errors
- if (data.isEmpty()) {
- // Set the empty text
- final NoResultsContainer empty = (NoResultsContainer)mRootView.findViewById(R.id.no_results_container);
- empty.setSecondaryText(R.string.empty_recent);
- mListView.setEmptyView(empty);
- return;
- }
-
- // Start fresh
- mAdapter.unload();
- // Add the data to the adpater
- for (final Album album : data) {
- mAdapter.add(album);
+ public void restartLoader() {
+ // Update the list when the user deletes any items
+ if (mShouldRefresh) {
+ getLoaderManager().restartLoader(LOADER, null, this);
}
- // Build the cache
- mAdapter.buildCache();
+ mShouldRefresh = false;
}
/**
* {@inheritDoc}
*/
@Override
- public void onLoaderReset(final Loader<List<Album>> loader) {
- // Clear the data in the adapter
- mAdapter.unload();
+ public void onMetaChanged() {
+ getLoaderManager().restartLoader(LOADER, null, this);
}
- /**
- * {@inheritDoc}
- */
@Override
- public void onScroll(final AbsListView view, final int firstVisibleItem,
- final int visibleItemCount, final int totalItemCount) {
- // Nothing to do
+ public int getGroupId() {
+ return GROUP_ID;
}
- /**
- * {@inheritDoc}
- */
@Override
- public void restartLoader() {
- // Update the list when the user deletes any items
- if (mShouldRefresh) {
- getLoaderManager().restartLoader(LOADER, null, this);
- }
- mShouldRefresh = false;
+ public int getLoaderId() {
+ return LOADER;
}
- /**
- * {@inheritDoc}
- */
@Override
- public void onMetaChanged() {
- getLoaderManager().restartLoader(LOADER, null, this);
+ public void playAll(int position) {
+ Cursor cursor = TopTracksLoader.makeRecentTracksCursor(getActivity());
+ final long[] list = MusicUtils.getSongListForCursor(cursor);
+ MusicUtils.playAll(getActivity(), list, position, false);
+ cursor.close();
+ cursor = null;
}
- /**
- * Sets up various helpers for both the list and grid
- *
- * @param list The list or grid
- */
- private void initAbsListView(final AbsListView list) {
- // Release any references to the recycled Views
- list.setRecyclerListener(new RecycleHolder());
- // Listen for ContextMenus to be created
- list.setOnCreateContextMenuListener(this);
- // Show the albums and songs from the selected artist
- list.setOnItemClickListener(this);
- // To help make scrolling smooth
- list.setOnScrollListener(this);
- }
+ @Override
+ public void setupNoResultsContainer(NoResultsContainer empty) {
+ super.setupNoResultsContainer(empty);
- /**
- * Sets up the list view
- */
- private void initListView() {
- // Initialize the grid
- mListView = (ListView)mRootView.findViewById(R.id.list_base);
- // Set the data behind the list
- mListView.setAdapter(mAdapter);
- // Set up the helpers
- initAbsListView(mListView);
- mAdapter.setTouchPlay(true);
+ empty.setMainText(R.string.empty_recent_main);
+ empty.setSecondaryText(R.string.empty_recent);
}
}
+
diff --git a/src/com/cyngn/eleven/ui/fragments/profile/BasicSongFragment.java b/src/com/cyngn/eleven/ui/fragments/profile/BasicSongFragment.java
index 549024e..c32d99b 100644
--- a/src/com/cyngn/eleven/ui/fragments/profile/BasicSongFragment.java
+++ b/src/com/cyngn/eleven/ui/fragments/profile/BasicSongFragment.java
@@ -276,6 +276,9 @@ public abstract class BasicSongFragment extends Fragment implements LoaderCallba
if (data.isEmpty()) {
// Set the empty text
final NoResultsContainer empty = (NoResultsContainer)mRootView.findViewById(R.id.no_results_container);
+ // Setup the container strings
+ setupNoResultsContainer(empty);
+ // set the empty view into the list view
mListView.setEmptyView(empty);
return;
}
@@ -305,7 +308,7 @@ public abstract class BasicSongFragment extends Fragment implements LoaderCallba
}
/**
- * If the subclasses want to use a customized SongADapter
+ * If the subclasses want to use a customized SongAdapter
* @return the Song adapter
*/
protected SongAdapter createAdapter() {
diff --git a/src/com/cyngn/eleven/ui/fragments/profile/TopTracksFragment.java b/src/com/cyngn/eleven/ui/fragments/profile/TopTracksFragment.java
index 1ec2e93..00a53bc 100644
--- a/src/com/cyngn/eleven/ui/fragments/profile/TopTracksFragment.java
+++ b/src/com/cyngn/eleven/ui/fragments/profile/TopTracksFragment.java
@@ -51,7 +51,7 @@ public class TopTracksFragment extends BasicSongFragment {
*/
@Override
public Loader<List<Song>> onCreateLoader(final int id, final Bundle args) {
- return new TopTracksLoader(getActivity());
+ return new TopTracksLoader(getActivity(), TopTracksLoader.QueryType.TopTracks);
}
@Override
diff --git a/src/com/cyngn/eleven/utils/MusicUtils.java b/src/com/cyngn/eleven/utils/MusicUtils.java
index 7ee3b8f..7a86890 100644
--- a/src/com/cyngn/eleven/utils/MusicUtils.java
+++ b/src/com/cyngn/eleven/utils/MusicUtils.java
@@ -1289,17 +1289,6 @@ 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
- */
- public static final String getLastAlbumForArtist(final Context context, final String artistName) {
- return RecentStore.getInstance(context).getAlbumName(artistName);
- }
-
- /**
* Seeks the current track to a desired position
*
* @param position The position to seek to
@@ -1419,7 +1408,7 @@ public final class MusicUtils {
// 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();
}
@@ -1546,4 +1535,4 @@ public final class MusicUtils {
}
return true;
}
-} \ No newline at end of file
+}