aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorScott Jackson <daneren2005@gmail.com>2014-08-24 16:12:58 -0700
committerScott Jackson <daneren2005@gmail.com>2014-08-24 16:12:58 -0700
commit050a93815b028eb570f6a5d065d502878de55e4c (patch)
treee0fac20abdd82359bc0770366f9a65b877867c79
parentbc4bac7a7c2e02e0f65ae804205232f2e256b97a (diff)
parent67c94f2bd63c65937386991f7232aa053acc101d (diff)
downloaddsub-050a93815b028eb570f6a5d065d502878de55e4c.tar.gz
dsub-050a93815b028eb570f6a5d065d502878de55e4c.tar.bz2
dsub-050a93815b028eb570f6a5d065d502878de55e4c.zip
Merge branch 'Bookmarks' of https://github.com/daneren2005/Subsonic
-rw-r--r--res/menu/select_bookmark_context.xml8
-rw-r--r--res/menu/select_song_context.xml6
-rw-r--r--res/values/strings.xml4
-rw-r--r--src/github/daneren2005/dsub/activity/SubsonicFragmentActivity.java34
-rw-r--r--src/github/daneren2005/dsub/domain/Bookmark.java16
-rw-r--r--src/github/daneren2005/dsub/domain/MusicDirectory.java56
-rw-r--r--src/github/daneren2005/dsub/fragments/NowPlayingFragment.java13
-rw-r--r--src/github/daneren2005/dsub/fragments/SelectBookmarkFragment.java64
-rw-r--r--src/github/daneren2005/dsub/fragments/SelectDirectoryFragment.java19
-rw-r--r--src/github/daneren2005/dsub/fragments/SubsonicFragment.java229
-rw-r--r--src/github/daneren2005/dsub/service/CachedMusicService.java327
-rw-r--r--src/github/daneren2005/dsub/service/DownloadService.java142
-rw-r--r--src/github/daneren2005/dsub/service/MusicService.java6
-rw-r--r--src/github/daneren2005/dsub/service/OfflineMusicService.java7
-rw-r--r--src/github/daneren2005/dsub/service/RESTMusicService.java6
-rw-r--r--src/github/daneren2005/dsub/service/parser/BookmarkParser.java15
-rw-r--r--src/github/daneren2005/dsub/service/parser/MusicDirectoryEntryParser.java7
-rw-r--r--src/github/daneren2005/dsub/service/parser/PodcastEntryParser.java6
-rw-r--r--src/github/daneren2005/dsub/util/FileUtil.java4
-rw-r--r--src/github/daneren2005/dsub/util/Util.java14
-rw-r--r--src/github/daneren2005/dsub/view/BookmarkAdapter.java9
21 files changed, 775 insertions, 217 deletions
diff --git a/res/menu/select_bookmark_context.xml b/res/menu/select_bookmark_context.xml
index 724c4c78..2b1b83fd 100644
--- a/res/menu/select_bookmark_context.xml
+++ b/res/menu/select_bookmark_context.xml
@@ -5,6 +5,14 @@
<item
android:id="@+id/bookmark_menu_info"
android:title="@string/common.info"/>
+
+ <item
+ android:id="@+id/song_menu_show_album"
+ android:title="@string/download.menu_show_album"/>
+
+ <item
+ android:id="@+id/song_menu_show_artist"
+ android:title="@string/menu.show_artist"/>
<item
android:id="@+id/song_menu_download"
diff --git a/res/menu/select_song_context.xml b/res/menu/select_song_context.xml
index 94283f70..5123a0ae 100644
--- a/res/menu/select_song_context.xml
+++ b/res/menu/select_song_context.xml
@@ -59,4 +59,10 @@
android:id="@+id/song_menu_share"
android:title="@string/menu.share"/>
</group>
+
+ <group android:id="@+id/server_1.9">
+ <item
+ android:id="@+id/bookmark_menu_delete"
+ android:title="@string/bookmark.delete"/>
+ </group>
</menu>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 71a74db6..ffd6ba7f 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -237,6 +237,10 @@
\nCreated: %3$s
\nLast Updated: %4$s
\nComment: %5$s</string>
+ <string name="bookmark.resume_title">Resume playing?</string>
+ <string name="bookmark.resume">Resume playing \'%1$s\' from %2$s</string>
+ <string name="bookmark.action_resume">Resume</string>
+ <string name="bookmark.action_start_over">Start Over</string>
<string name="song_details.error">Error</string>
<string name="song_details.skipped">Skipped</string>
diff --git a/src/github/daneren2005/dsub/activity/SubsonicFragmentActivity.java b/src/github/daneren2005/dsub/activity/SubsonicFragmentActivity.java
index 30cabd76..3d1f8aab 100644
--- a/src/github/daneren2005/dsub/activity/SubsonicFragmentActivity.java
+++ b/src/github/daneren2005/dsub/activity/SubsonicFragmentActivity.java
@@ -44,6 +44,7 @@ import java.util.concurrent.TimeUnit;
import github.daneren2005.dsub.R;
import github.daneren2005.dsub.domain.MusicDirectory;
import github.daneren2005.dsub.domain.PlayerState;
+import github.daneren2005.dsub.domain.ServerInfo;
import github.daneren2005.dsub.fragments.AdminFragment;
import github.daneren2005.dsub.fragments.ChatFragment;
import github.daneren2005.dsub.fragments.DownloadFragment;
@@ -58,6 +59,8 @@ import github.daneren2005.dsub.fragments.SelectShareFragment;
import github.daneren2005.dsub.fragments.SubsonicFragment;
import github.daneren2005.dsub.service.DownloadFile;
import github.daneren2005.dsub.service.DownloadService;
+import github.daneren2005.dsub.service.MusicService;
+import github.daneren2005.dsub.service.MusicServiceFactory;
import github.daneren2005.dsub.updates.Updater;
import github.daneren2005.dsub.util.BackgroundTask;
import github.daneren2005.dsub.util.Constants;
@@ -73,6 +76,7 @@ import github.daneren2005.dsub.view.ChangeLog;
public class SubsonicFragmentActivity extends SubsonicActivity {
private static String TAG = SubsonicFragmentActivity.class.getSimpleName();
private static boolean infoDialogDisplayed;
+ private static boolean sessionInitialized = false;
private ScheduledExecutorService executorService;
private View bottomBar;
private View coverArtView;
@@ -119,7 +123,9 @@ public class SubsonicFragmentActivity extends SubsonicActivity {
if("".equals(fragmentType) || fragmentType == null || firstRun) {
// Initial startup stuff
- loadSettings();
+ if(!sessionInitialized) {
+ loadSession();
+ }
}
currentFragment.setPrimaryFragment(true);
@@ -465,6 +471,14 @@ public class SubsonicFragmentActivity extends SubsonicActivity {
}
}
+ private void loadSession() {
+ loadSettings();
+ if(!Util.isOffline(this) && ServerInfo.checkServerVersion(this, "1.9")) {
+ loadBookmarks();
+ }
+
+ sessionInitialized = true;
+ }
private void loadSettings() {
PreferenceManager.setDefaultValues(this, R.xml.settings, false);
SharedPreferences prefs = Util.getPreferences(this);
@@ -502,6 +516,24 @@ public class SubsonicFragmentActivity extends SubsonicActivity {
editor.putString(Constants.PREFERENCES_KEY_CACHE_LOCATION, FileUtil.getDefaultMusicDirectory(this).getPath());
editor.commit();
}
+
+ private void loadBookmarks() {
+ final Context context = this;
+ new SilentBackgroundTask<Void>(context) {
+ @Override
+ public Void doInBackground() throws Throwable {
+ MusicService musicService = MusicServiceFactory.getMusicService(context);
+ musicService.getBookmarks(true, context, null);
+
+ return null;
+ }
+
+ @Override
+ public void error(Throwable error) {
+ Log.e(TAG, "Failed to get bookmarks", error);
+ }
+ }.execute();
+ }
private void createAccount() {
final Context context = this;
diff --git a/src/github/daneren2005/dsub/domain/Bookmark.java b/src/github/daneren2005/dsub/domain/Bookmark.java
index 4092cec4..4a86b1bd 100644
--- a/src/github/daneren2005/dsub/domain/Bookmark.java
+++ b/src/github/daneren2005/dsub/domain/Bookmark.java
@@ -33,7 +33,13 @@ public class Bookmark implements Serializable {
private String comment;
private Date created;
private Date changed;
- private MusicDirectory.Entry entry;
+
+ public Bookmark() {
+
+ }
+ public Bookmark(int position) {
+ this.position = position;
+ }
public int getPosition() {
return position;
@@ -90,12 +96,4 @@ public class Bookmark implements Serializable {
this.changed = null;
}
}
-
- public MusicDirectory.Entry getEntry() {
- return this.entry;
- }
-
- public void setEntry(MusicDirectory.Entry entry) {
- this.entry = entry;
- }
}
diff --git a/src/github/daneren2005/dsub/domain/MusicDirectory.java b/src/github/daneren2005/dsub/domain/MusicDirectory.java
index 1b3342bd..0cff955d 100644
--- a/src/github/daneren2005/dsub/domain/MusicDirectory.java
+++ b/src/github/daneren2005/dsub/domain/MusicDirectory.java
@@ -107,30 +107,32 @@ public class MusicDirectory implements Serializable {
}
public static class Entry implements Serializable {
- private String id;
- private String parent;
+ private String id;
+ private String parent;
private String grandParent;
private String albumId;
private String artistId;
- private boolean directory;
- private String title;
- private String album;
- private String artist;
- private Integer track;
- private Integer year;
- private String genre;
- private String contentType;
- private String suffix;
- private String transcodedContentType;
- private String transcodedSuffix;
- private String coverArt;
- private Long size;
- private Integer duration;
- private Integer bitRate;
- private String path;
- private boolean video;
+ private boolean directory;
+ private String title;
+ private String album;
+ private String artist;
+ private Integer track;
+ private Integer year;
+ private String genre;
+ private String contentType;
+ private String suffix;
+ private String transcodedContentType;
+ private String transcodedSuffix;
+ private String coverArt;
+ private Long size;
+ private Integer duration;
+ private Integer bitRate;
+ private String path;
+ private boolean video;
private Integer discNumber;
- private boolean starred;
+ private boolean starred;
+ private Integer rating;
+ private Bookmark bookmark;
private int closeness;
public void loadMetadata(File file) {
@@ -392,6 +394,20 @@ public class MusicDirectory implements Serializable {
public void setStarred(boolean starred) {
this.starred = starred;
}
+
+ public Integer getRating() {
+ return rating;
+ }
+ public void setRating(Integer rating) {
+ this.rating = rating;
+ }
+
+ public Bookmark getBookmark() {
+ return bookmark;
+ }
+ public void setBookmark(Bookmark bookmark) {
+ this.bookmark = bookmark;
+ }
public int getCloseness() {
return closeness;
diff --git a/src/github/daneren2005/dsub/fragments/NowPlayingFragment.java b/src/github/daneren2005/dsub/fragments/NowPlayingFragment.java
index 3486f154..90cc746c 100644
--- a/src/github/daneren2005/dsub/fragments/NowPlayingFragment.java
+++ b/src/github/daneren2005/dsub/fragments/NowPlayingFragment.java
@@ -30,7 +30,6 @@ import android.os.Bundle;
import android.os.Handler;
import android.support.v4.view.MenuItemCompat;
import android.support.v7.app.MediaRouteButton;
-import android.util.Log;
import android.view.ContextMenu;
import android.view.Display;
import android.view.GestureDetector;
@@ -46,7 +45,6 @@ import android.view.ViewGroup;
import android.view.WindowManager;
import android.view.animation.AnimationUtils;
import android.widget.AdapterView;
-import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageButton;
@@ -57,6 +55,7 @@ import android.widget.TextView;
import android.widget.ViewFlipper;
import github.daneren2005.dsub.R;
import github.daneren2005.dsub.activity.SubsonicFragmentActivity;
+import github.daneren2005.dsub.domain.Bookmark;
import github.daneren2005.dsub.domain.MusicDirectory;
import github.daneren2005.dsub.domain.PlayerState;
import github.daneren2005.dsub.domain.RepeatMode;
@@ -68,7 +67,6 @@ import github.daneren2005.dsub.util.Constants;
import github.daneren2005.dsub.util.SilentBackgroundTask;
import github.daneren2005.dsub.view.DownloadFileAdapter;
import github.daneren2005.dsub.view.FadeOutAnimation;
-import github.daneren2005.dsub.view.SongView;
import github.daneren2005.dsub.view.UpdateView;
import github.daneren2005.dsub.util.Util;
import github.daneren2005.dsub.view.VisualizerView;
@@ -1321,7 +1319,14 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis
protected Void doInBackground() throws Throwable {
MusicDirectory.Entry currentSong = currentDownload.getSong();
MusicService musicService = MusicServiceFactory.getMusicService(context);
- musicService.createBookmark(currentSong.getId(), getDownloadService().getPlayerPosition(), comment, context, null);
+ int position = getDownloadService().getPlayerPosition();
+ musicService.createBookmark(currentSong.getId(), Util.getParentFromEntry(context, currentSong), position, comment, context, null);
+
+ currentSong.setBookmark(new Bookmark(position));
+ MusicDirectory.Entry find = UpdateView.findEntry(currentSong);
+ if(find != null && find != currentSong) {
+ find.setBookmark(new Bookmark(position));
+ }
return null;
}
diff --git a/src/github/daneren2005/dsub/fragments/SelectBookmarkFragment.java b/src/github/daneren2005/dsub/fragments/SelectBookmarkFragment.java
index 5ea432e5..f9ab0cc9 100644
--- a/src/github/daneren2005/dsub/fragments/SelectBookmarkFragment.java
+++ b/src/github/daneren2005/dsub/fragments/SelectBookmarkFragment.java
@@ -41,9 +41,10 @@ import github.daneren2005.dsub.util.Util;
import github.daneren2005.dsub.view.BookmarkAdapter;
import java.text.Format;
import java.text.SimpleDateFormat;
+import java.util.Arrays;
import java.util.List;
-public class SelectBookmarkFragment extends SelectListFragment<Bookmark> {
+public class SelectBookmarkFragment extends SelectListFragment<MusicDirectory.Entry> {
private static final String TAG = SelectBookmarkFragment.class.getSimpleName();
@Override
@@ -57,18 +58,18 @@ public class SelectBookmarkFragment extends SelectListFragment<Bookmark> {
@Override
public boolean onContextItemSelected(MenuItem menuItem) {
AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuItem.getMenuInfo();
- Bookmark bookmark = objects.get(info.position);
+ MusicDirectory.Entry bookmark = objects.get(info.position);
switch(menuItem.getItemId()) {
case R.id.bookmark_menu_info:
displayBookmarkInfo(bookmark);
- break;
+ return true;
case R.id.bookmark_menu_delete:
- deleteBookmark(bookmark);
- break;
+ deleteBookmark(bookmark, adapter);
+ return true;
}
- if(onContextItemSelected(menuItem, bookmark.getEntry())) {
+ if(onContextItemSelected(menuItem, bookmark)) {
return true;
}
@@ -81,13 +82,13 @@ public class SelectBookmarkFragment extends SelectListFragment<Bookmark> {
}
@Override
- public ArrayAdapter getAdapter(List<Bookmark> bookmarks) {
+ public ArrayAdapter getAdapter(List<MusicDirectory.Entry> bookmarks) {
return new BookmarkAdapter(context, bookmarks);
}
@Override
- public List<Bookmark> getObjects(MusicService musicService, boolean refresh, ProgressListener listener) throws Exception {
- return musicService.getBookmarks(refresh, context, listener);
+ public List<MusicDirectory.Entry> getObjects(MusicService musicService, boolean refresh, ProgressListener listener) throws Exception {
+ return musicService.getBookmarks(refresh, context, listener).getChildren();
}
@Override
@@ -102,11 +103,12 @@ public class SelectBookmarkFragment extends SelectListFragment<Bookmark> {
return;
}
- final Bookmark bookmark = (Bookmark) parent.getItemAtPosition(position);
+ final MusicDirectory.Entry bookmark = (MusicDirectory.Entry) parent.getItemAtPosition(position);
new SilentBackgroundTask<Void>(context) {
@Override
protected Void doInBackground() throws Throwable {
- downloadService.download(bookmark);
+ downloadService.clear();
+ downloadService.download(Arrays.asList(bookmark), false, true, false, false, 0, bookmark.getBookmark().getPosition());
return null;
}
@@ -117,7 +119,8 @@ public class SelectBookmarkFragment extends SelectListFragment<Bookmark> {
}.execute();
}
- private void displayBookmarkInfo(final Bookmark bookmark) {
+ private void displayBookmarkInfo(final MusicDirectory.Entry entry) {
+ Bookmark bookmark = entry.getBookmark();
Format formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String comment = bookmark.getComment();
if(comment == null) {
@@ -125,44 +128,9 @@ public class SelectBookmarkFragment extends SelectListFragment<Bookmark> {
}
String msg = context.getResources().getString(R.string.bookmark_details,
- bookmark.getEntry().getTitle(), Util.formatDuration(bookmark.getPosition() / 1000),
+ entry.getTitle(), Util.formatDuration(bookmark.getPosition() / 1000),
formatter.format(bookmark.getCreated()), formatter.format(bookmark.getChanged()), comment);
Util.info(context, R.string.bookmark_details_title, msg, false);
}
- private void deleteBookmark(final Bookmark bookmark) {
- final MusicDirectory.Entry entry = bookmark.getEntry();
- Util.confirmDialog(context, R.string.bookmark_delete_title, entry.getTitle(), new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- new LoadingTask<Void>(context, false) {
- @Override
- protected Void doInBackground() throws Throwable {
- MusicService musicService = MusicServiceFactory.getMusicService(context);
- musicService.deleteBookmark(entry.getId(), context, null);
- return null;
- }
-
- @Override
- protected void done(Void result) {
- adapter.remove(bookmark);
- adapter.notifyDataSetChanged();
- Util.toast(context, context.getResources().getString(R.string.bookmark_deleted, entry.getTitle()));
- }
-
- @Override
- protected void error(Throwable error) {
- String msg;
- if (error instanceof OfflineException || error instanceof ServerTooOldException) {
- msg = getErrorMessage(error);
- } else {
- msg = context.getResources().getString(R.string.bookmark_deleted_error, entry.getTitle()) + " " + getErrorMessage(error);
- }
-
- Util.toast(context, msg, false);
- }
- }.execute();
- }
- });
- }
}
diff --git a/src/github/daneren2005/dsub/fragments/SelectDirectoryFragment.java b/src/github/daneren2005/dsub/fragments/SelectDirectoryFragment.java
index ef9a6163..bb3c061a 100644
--- a/src/github/daneren2005/dsub/fragments/SelectDirectoryFragment.java
+++ b/src/github/daneren2005/dsub/fragments/SelectDirectoryFragment.java
@@ -359,10 +359,7 @@ public class SelectDirectoryFragment extends SubsonicFragment implements Adapter
songs.add((MusicDirectory.Entry) it.next());
}
- getDownloadService().clear();
- getDownloadService().download(songs, false, true, true, false);
- Util.startActivityWithoutTransition(context, DownloadActivity.class);
-
+ playNow(songs);
return true;
}
@@ -415,11 +412,7 @@ public class SelectDirectoryFragment extends SubsonicFragment implements Adapter
return;
}
- getDownloadService().clear();
- List<MusicDirectory.Entry> podcasts = new ArrayList<MusicDirectory.Entry>(1);
- podcasts.add(entry);
- getDownloadService().download(podcasts, false, true, true, false);
- Util.startActivityWithoutTransition(context, DownloadActivity.class);
+ playNow(Arrays.asList(entry));
}
}
}
@@ -844,6 +837,14 @@ public class SelectDirectoryFragment extends SubsonicFragment implements Adapter
final List<MusicDirectory.Entry> songs = getSelectedSongs();
warnIfNetworkOrStorageUnavailable();
+
+ // Conditions for using play now button
+ if(!append && !save && autoplay && !playNext && !shuffle) {
+ // Call playNow which goes through and tries to use bookmark information
+ playNow(songs);
+ return;
+ }
+
LoadingTask<Void> onValid = new LoadingTask<Void>(context) {
@Override
protected Void doInBackground() throws Throwable {
diff --git a/src/github/daneren2005/dsub/fragments/SubsonicFragment.java b/src/github/daneren2005/dsub/fragments/SubsonicFragment.java
index 580016bb..216f526c 100644
--- a/src/github/daneren2005/dsub/fragments/SubsonicFragment.java
+++ b/src/github/daneren2005/dsub/fragments/SubsonicFragment.java
@@ -39,6 +39,7 @@ import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.widget.AbsListView;
+import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.EditText;
@@ -83,6 +84,8 @@ import java.util.LinkedList;
import java.util.List;
import java.util.Random;
+import static github.daneren2005.dsub.domain.MusicDirectory.Entry;
+
public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnRefreshListener {
private static final String TAG = SubsonicFragment.class.getSimpleName();
private static int TAG_INC = 10;
@@ -162,8 +165,8 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR
public void onCreateContextMenu(ContextMenu menu, View view, ContextMenu.ContextMenuInfo menuInfo, Object selected) {
MenuInflater inflater = context.getMenuInflater();
- if(selected instanceof MusicDirectory.Entry) {
- MusicDirectory.Entry entry = (MusicDirectory.Entry) selected;
+ if(selected instanceof Entry) {
+ Entry entry = (Entry) selected;
if(entry instanceof PodcastEpisode && !entry.isVideo()) {
if(Util.isOffline(context)) {
inflater.inflate(R.menu.select_podcast_episode_context_offline, menu);
@@ -186,6 +189,10 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR
}
else {
inflater.inflate(R.menu.select_song_context, menu);
+
+ if(entry.getBookmark() == null) {
+ menu.removeItem(R.id.bookmark_menu_delete);
+ }
}
menu.findItem(entry.isDirectory() ? R.id.album_menu_star : R.id.song_menu_star).setTitle(entry.isStarred() ? R.string.common_unstar : R.string.common_star);
} else {
@@ -216,6 +223,9 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR
menu.setGroupVisible(R.id.server_1_8, false);
menu.setGroupVisible(R.id.hide_star, false);
}
+ if(!ServerInfo.checkServerVersion(context, "1.9")) {
+ menu.setGroupVisible(R.id.server_1_9, false);
+ }
if(!ServerInfo.checkServerVersion(context, "1.10.1")) {
menu.setGroupVisible(R.id.server_1_10, false);
}
@@ -252,8 +262,8 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR
public boolean onContextItemSelected(MenuItem menuItem, Object selectedItem) {
Artist artist = selectedItem instanceof Artist ? (Artist) selectedItem : null;
- MusicDirectory.Entry entry = selectedItem instanceof MusicDirectory.Entry ? (MusicDirectory.Entry) selectedItem : null;
- List<MusicDirectory.Entry> songs = new ArrayList<MusicDirectory.Entry>(1);
+ Entry entry = selectedItem instanceof Entry ? (Entry) selectedItem : null;
+ List<Entry> songs = new ArrayList<Entry>(1);
songs.add(entry);
switch (menuItem.getItemId()) {
@@ -315,15 +325,13 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR
displaySongInfo(entry);
break;
case R.id.album_menu_show_artist:
- showArtist((MusicDirectory.Entry) selectedItem);
+ showAlbumArtist((Entry) selectedItem);
break;
case R.id.album_menu_share:
createShare(songs);
break;
case R.id.song_menu_play_now:
- getDownloadService().clear();
- getDownloadService().download(songs, false, true, true, false);
- Util.startActivityWithoutTransition(context, DownloadActivity.class);
+ playNow(songs);
break;
case R.id.song_menu_play_next:
getDownloadService().download(songs, false, false, true, false);
@@ -358,6 +366,15 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR
case R.id.song_menu_share:
createShare(songs);
break;
+ case R.id.song_menu_show_album:
+ showAlbum((Entry) selectedItem);
+ break;
+ case R.id.song_menu_show_artist:
+ showArtist((Entry) selectedItem);
+ break;
+ case R.id.bookmark_menu_delete:
+ deleteBookmark(entry, null);
+ break;
default:
return false;
}
@@ -636,7 +653,7 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR
dialog.show();
}
- public void toggleStarred(final MusicDirectory.Entry entry) {
+ public void toggleStarred(final Entry entry) {
final boolean starred = !entry.isStarred();
entry.setStarred(starred);
@@ -692,12 +709,12 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR
}
}.execute();
}
- protected void setEntryStarred(MusicDirectory.Entry entry, boolean starred) {
+ protected void setEntryStarred(Entry entry, boolean starred) {
DownloadService downloadService = DownloadService.getInstance();
if(downloadService != null && !entry.isDirectory()) {
List<DownloadFile> files = downloadService.getDownloads();
for(DownloadFile file: files) {
- MusicDirectory.Entry check = file.getSong();
+ Entry check = file.getSong();
if(entry.getId().equals(check.getId())) {
check.setStarred(starred);
downloadService.serializeQueue();
@@ -706,7 +723,7 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR
}
}
- MusicDirectory.Entry find = UpdateView.findEntry(entry);
+ Entry find = UpdateView.findEntry(entry);
if(find != null) {
find.setStarred(starred);
}
@@ -789,7 +806,7 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR
Collections.shuffle(root.getChildren());
}
- List<MusicDirectory.Entry> songs = new LinkedList<MusicDirectory.Entry>();
+ List<Entry> songs = new LinkedList<Entry>();
getSongsRecursively(root, songs);
DownloadService downloadService = getDownloadService();
@@ -813,17 +830,17 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR
return transition;
}
- private void getSongsRecursively(MusicDirectory parent, List<MusicDirectory.Entry> songs) throws Exception {
+ private void getSongsRecursively(MusicDirectory parent, List<Entry> songs) throws Exception {
if (songs.size() > MAX_SONGS) {
return;
}
- for (MusicDirectory.Entry song : parent.getChildren(false, true)) {
+ for (Entry song : parent.getChildren(false, true)) {
if (!song.isVideo()) {
songs.add(song);
}
}
- for (MusicDirectory.Entry dir : parent.getChildren(true, false)) {
+ for (Entry dir : parent.getChildren(true, false)) {
MusicService musicService = MusicServiceFactory.getMusicService(context);
MusicDirectory musicDirectory;
@@ -861,7 +878,7 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR
}
}
- protected void addToPlaylist(final List<MusicDirectory.Entry> songs) {
+ protected void addToPlaylist(final List<Entry> songs) {
if(songs.isEmpty()) {
Util.toast(context, "No songs selected");
return;
@@ -925,7 +942,7 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR
}.execute();
}
- private void addToPlaylist(final Playlist playlist, final List<MusicDirectory.Entry> songs) {
+ private void addToPlaylist(final Playlist playlist, final List<Entry> songs) {
new SilentBackgroundTask<Void>(context) {
@Override
protected Void doInBackground() throws Throwable {
@@ -953,7 +970,7 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR
}.execute();
}
- protected void createNewPlaylist(final List<MusicDirectory.Entry> songs, final boolean getSuggestion) {
+ protected void createNewPlaylist(final List<Entry> songs, final boolean getSuggestion) {
View layout = context.getLayoutInflater().inflate(R.layout.save_playlist, null);
final EditText playlistNameView = (EditText) layout.findViewById(R.id.save_playlist_name);
final CheckBox overwriteCheckBox = (CheckBox) layout.findViewById(R.id.save_playlist_overwrite);
@@ -1011,7 +1028,7 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR
AlertDialog dialog = builder.create();
dialog.show();
}
- private void createNewPlaylist(final List<MusicDirectory.Entry> songs, final String name) {
+ private void createNewPlaylist(final List<Entry> songs, final String name) {
new SilentBackgroundTask<Void>(context) {
@Override
protected Void doInBackground() throws Throwable {
@@ -1032,13 +1049,13 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR
}
}.execute();
}
- private void overwritePlaylist(final List<MusicDirectory.Entry> songs, final String name, final String id) {
+ private void overwritePlaylist(final List<Entry> songs, final String name, final String id) {
new SilentBackgroundTask<Void>(context) {
@Override
protected Void doInBackground() throws Throwable {
MusicService musicService = MusicServiceFactory.getMusicService(context);
MusicDirectory playlist = musicService.getPlaylist(true, id, name, context, null);
- List<MusicDirectory.Entry> toDelete = playlist.getChildren();
+ List<Entry> toDelete = playlist.getChildren();
musicService.overwritePlaylist(id, name, toDelete.size(), songs, context, null);
return null;
}
@@ -1062,7 +1079,7 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR
}.execute();
}
- public void displaySongInfo(final MusicDirectory.Entry song) {
+ public void displaySongInfo(final Entry song) {
Integer bitrate = null;
String format = null;
long size = 0;
@@ -1134,7 +1151,7 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR
Util.info(context, song.getTitle(), msg);
}
- protected void playVideo(MusicDirectory.Entry entry) {
+ protected void playVideo(Entry entry) {
if(entryExists(entry)) {
playExternalPlayer(entry);
} else {
@@ -1142,7 +1159,7 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR
}
}
- protected void playWebView(MusicDirectory.Entry entry) {
+ protected void playWebView(Entry entry) {
int maxBitrate = Util.getMaxVideoBitrate(context);
Intent intent = new Intent(Intent.ACTION_VIEW);
@@ -1150,7 +1167,7 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR
startActivity(intent);
}
- protected void playExternalPlayer(MusicDirectory.Entry entry) {
+ protected void playExternalPlayer(Entry entry) {
if(!entryExists(entry)) {
Util.toast(context, R.string.download_need_download);
} else {
@@ -1166,7 +1183,7 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR
}
}
}
- protected void streamExternalPlayer(MusicDirectory.Entry entry) {
+ protected void streamExternalPlayer(Entry entry) {
String videoPlayerType = Util.getVideoPlayerType(context);
if("flash".equals(videoPlayerType)) {
playWebView(entry);
@@ -1178,7 +1195,7 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR
streamExternalPlayer(entry, entry.getTranscodedSuffix());
}
}
- protected void streamExternalPlayer(MusicDirectory.Entry entry, String format) {
+ protected void streamExternalPlayer(Entry entry, String format) {
try {
int maxBitrate = Util.getMaxVideoBitrate(context);
@@ -1208,7 +1225,7 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR
}
}
- protected boolean entryExists(MusicDirectory.Entry entry) {
+ protected boolean entryExists(Entry entry) {
DownloadFile check = new DownloadFile(context, entry, false);
return check.isCompleteFileAvailable();
}
@@ -1224,7 +1241,7 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR
}
}
- public void deleteRecursively(MusicDirectory.Entry album) {
+ public void deleteRecursively(Entry album) {
File dir = FileUtil.getAlbumDirectory(context, album);
if(dir == null) return;
@@ -1235,7 +1252,7 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR
}
}
- public void showArtist(MusicDirectory.Entry entry) {
+ public void showAlbumArtist(Entry entry) {
SubsonicFragment fragment = new SelectDirectoryFragment();
Bundle args = new Bundle();
if(Util.isTagBrowsing(context)) {
@@ -1249,13 +1266,44 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR
replaceFragment(fragment, true);
}
+ public void showArtist(Entry entry) {
+ SubsonicFragment fragment = new SelectDirectoryFragment();
+ Bundle args = new Bundle();
+ if(Util.isTagBrowsing(context)) {
+ args.putString(Constants.INTENT_EXTRA_NAME_ID, entry.getArtistId());
+ } else {
+ if(entry.getGrandParent() == null) {
+ args.putString(Constants.INTENT_EXTRA_NAME_CHILD_ID, entry.getParent());
+ } else {
+ args.putString(Constants.INTENT_EXTRA_NAME_ID, entry.getGrandParent());
+ }
+ }
+ args.putString(Constants.INTENT_EXTRA_NAME_NAME, entry.getArtist());
+ args.putBoolean(Constants.INTENT_EXTRA_NAME_ARTIST, true);
+ fragment.setArguments(args);
+
+ replaceFragment(fragment, true);
+ }
+ public void showAlbum(Entry entry) {
+ SubsonicFragment fragment = new SelectDirectoryFragment();
+ Bundle args = new Bundle();
+ if(Util.isTagBrowsing(context)) {
+ args.putString(Constants.INTENT_EXTRA_NAME_ID, entry.getAlbumId());
+ } else {
+ args.putString(Constants.INTENT_EXTRA_NAME_ID, entry.getParent());
+ }
+ args.putString(Constants.INTENT_EXTRA_NAME_NAME, entry.getAlbum());
+ fragment.setArguments(args);
+
+ replaceFragment(fragment, true);
+ }
- public void createShare(final List<MusicDirectory.Entry> entries) {
+ public void createShare(final List<Entry> entries) {
new LoadingTask<List<Share>>(context, true) {
@Override
protected List<Share> doInBackground() throws Throwable {
List<String> ids = new ArrayList<String>(entries.size());
- for(MusicDirectory.Entry entry: entries) {
+ for(Entry entry: entries) {
ids.add(entry.getId());
}
@@ -1296,4 +1344,119 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR
public GestureDetector getGestureDetector() {
return gestureScanner;
}
+
+ protected void playBookmark(final List<Entry> songs, final Entry song) {
+ final Integer position = song.getBookmark().getPosition();
+
+ AlertDialog.Builder builder = new AlertDialog.Builder(context);
+ builder.setTitle(R.string.bookmark_resume_title)
+ .setMessage(getResources().getString(R.string.bookmark_resume, song.getTitle(), Util.formatDuration(position / 1000)))
+ .setPositiveButton(R.string.bookmark_action_resume, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int id) {
+ playNow(songs, song, position);
+ }
+ })
+ .setNegativeButton(R.string.bookmark_action_start_over, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int id) {
+ new SilentBackgroundTask<Void>(context) {
+ @Override
+ protected Void doInBackground() throws Throwable {
+ MusicService musicService = MusicServiceFactory.getMusicService(context);
+ musicService.deleteBookmark(song.getId(), Util.getParentFromEntry(context, song), context, null);
+
+ song.setBookmark(null);
+ return null;
+ }
+
+ @Override
+ protected void error(Throwable error) {
+ String msg;
+ if (error instanceof OfflineException || error instanceof ServerTooOldException) {
+ msg = getErrorMessage(error);
+ } else {
+ msg = context.getResources().getString(R.string.bookmark_deleted_error, song.getTitle()) + " " + getErrorMessage(error);
+ }
+
+ Util.toast(context, msg, false);
+ }
+ }.execute();
+
+ playNow(songs, 0);
+ }
+ });
+ AlertDialog dialog = builder.create();
+ dialog.show();
+ }
+
+ protected void playNow(List<Entry> entries) {
+ Entry bookmark = null;
+ for(Entry entry: entries) {
+ if(entry.getBookmark() != null) {
+ bookmark = entry;
+ break;
+ }
+ }
+
+ // If no bookmark found, just play from start
+ if(bookmark == null) {
+ playNow(entries, 0);
+ } else {
+ // If bookmark found, then give user choice to start from there or to start over
+ playBookmark(entries, bookmark);
+ }
+ }
+ protected void playNow(List<Entry> entries, int position) {
+ playNow(entries, entries.get(0), position);
+ }
+ protected void playNow(List<Entry> entries, Entry song, int position) {
+ DownloadService downloadService = getDownloadService();
+ if(downloadService == null) {
+ return;
+ }
+
+ downloadService.clear();
+ downloadService.download(entries, false, true, true, false, entries.indexOf(song), position);
+ Util.startActivityWithoutTransition(context, DownloadActivity.class);
+ }
+
+ protected void deleteBookmark(final MusicDirectory.Entry entry, final ArrayAdapter adapter) {
+ Util.confirmDialog(context, R.string.bookmark_delete_title, entry.getTitle(), new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ new LoadingTask<Void>(context, false) {
+ @Override
+ protected Void doInBackground() throws Throwable {
+ MusicService musicService = MusicServiceFactory.getMusicService(context);
+ musicService.deleteBookmark(entry.getId(), Util.getParentFromEntry(context, entry), context, null);
+
+ entry.setBookmark(null);
+ return null;
+ }
+
+ @Override
+ protected void done(Void result) {
+ if(adapter != null) {
+ adapter.remove(entry);
+ adapter.notifyDataSetChanged();
+ }
+ Util.toast(context, context.getResources().getString(R.string.bookmark_deleted, entry.getTitle()));
+ }
+
+ @Override
+ protected void error(Throwable error) {
+ String msg;
+ if (error instanceof OfflineException || error instanceof ServerTooOldException) {
+ msg = getErrorMessage(error);
+ } else {
+ msg = context.getResources().getString(R.string.bookmark_deleted_error, entry.getTitle()) + " " + getErrorMessage(error);
+ }
+
+ Util.toast(context, msg, false);
+ }
+ }.execute();
+ }
+ });
+ }
}
diff --git a/src/github/daneren2005/dsub/service/CachedMusicService.java b/src/github/daneren2005/dsub/service/CachedMusicService.java
index e97ffe24..90e5539c 100644
--- a/src/github/daneren2005/dsub/service/CachedMusicService.java
+++ b/src/github/daneren2005/dsub/service/CachedMusicService.java
@@ -360,17 +360,11 @@ public class CachedMusicService implements MusicService {
MusicDirectory oldDir = FileUtil.deserialize(context, "starred", MusicDirectory.class);
if(oldDir != null) {
- List<Entry> newList = new ArrayList<Entry>();
+ final List<Entry> newList = new ArrayList<Entry>();
newList.addAll(dir.getChildren());
- List<Entry> oldList = oldDir.getChildren();
+ final List<Entry> oldList = oldDir.getChildren();
- for(Iterator<Entry> it = oldList.iterator(); it.hasNext(); ) {
- // Remove any entries from newList
- if(newList.remove(it.next())) {
- // If it was removed, then remove from oldList as well
- it.remove();
- }
- }
+ removeDuplicates(oldList, newList);
// Left overs in newList need to be starred
boolean isTagBrowsing = Util.isTagBrowsing(context, musicService.getInstance(context));
@@ -378,6 +372,48 @@ public class CachedMusicService implements MusicService {
// Left overs in oldList need to be unstarred
updateStarredList(context, oldList, false, isTagBrowsing);
+
+
+ // Remove non-songs from lists before updating playlists
+ for(Iterator<Entry> it = oldList.iterator(); it.hasNext(); ) {
+ if(it.next().isDirectory()) {
+ it.remove();
+ }
+ }
+ for(Iterator<Entry> it = newList.iterator(); it.hasNext(); ) {
+ if(it.next().isDirectory()) {
+ it.remove();
+ }
+ }
+
+ // Only try to update playlists if there was at least one song in new or old set
+ if(newList.size() > 0 || oldList.size() > 0) {
+ new PlaylistDirectoryUpdater(context) {
+ @Override
+ public boolean checkResult(Entry check) {
+ for(Entry entry: oldList) {
+ if(check.getId().equals(entry.getId()) && check.isStarred() != false) {
+ check.setStarred(false);
+ return true;
+ }
+ }
+
+ for(Entry entry: newList) {
+ if(check.getId().equals(entry.getId()) && check.isStarred() != true) {
+ check.setStarred(true);
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ @Override
+ public void updateResult(Entry result) {
+
+ }
+ }.execute();
+ }
}
FileUtil.serialize(context, dir, "starred");
@@ -386,11 +422,6 @@ public class CachedMusicService implements MusicService {
private void updateStarredList(Context context, List<Entry> list, final boolean starred, final boolean isTagBrowsing) {
for(final Entry entry: list) {
- // Don't waste time when status is the same
- if(entry.isStarred() == starred) {
- continue;
- }
-
String cacheName, parent = null;
boolean isArtist = false;
if(isTagBrowsing) {
@@ -420,7 +451,7 @@ public class CachedMusicService implements MusicService {
new IndexesUpdater(context, isTagBrowsing ? "artists" : "indexes") {
@Override
public boolean checkResult(Artist check) {
- if(entry.getId().equals(check.getId())) {
+ if(entry.getId().equals(check.getId()) && check.isStarred() != starred) {
return true;
}
@@ -436,7 +467,7 @@ public class CachedMusicService implements MusicService {
new MusicDirectoryUpdater(context, cacheName, parent) {
@Override
public boolean checkResult(Entry check) {
- if (entry.getId().equals(check.getId())) {
+ if (entry.getId().equals(check.getId()) && check.isStarred() != starred) {
return true;
}
@@ -562,7 +593,7 @@ public class CachedMusicService implements MusicService {
}
for (String parent : parents) {
- new MusicDirectoryUpdater(context, cacheName, parent) {
+ new MusicDirectoryUpdater(context, cacheName, parent, checkIds.size() == 1) {
@Override
public boolean checkResult(Entry check) {
for (String id : checkIds) {
@@ -603,26 +634,23 @@ public class CachedMusicService implements MusicService {
// Update playlist caches if there is at least one song to be starred
if(ids != null && ids.size() > 0) {
- List<Playlist> playlists = FileUtil.deserialize(context, getCacheName(context, "playlist"), ArrayList.class);
- for(Playlist playlist: playlists) {
- new MusicDirectoryUpdater(context, "playlist", playlist.getId()) {
- @Override
- public boolean checkResult(Entry check) {
- for (String id : checkIds) {
- if (id.equals(check.getId())) {
- return true;
- }
+ new PlaylistDirectoryUpdater(context) {
+ @Override
+ public boolean checkResult(Entry check) {
+ for (String id : checkIds) {
+ if (id.equals(check.getId())) {
+ return true;
}
-
- return false;
}
- @Override
- public void updateResult(List<Entry> objects, Entry result) {
- result.setStarred(starred);
- }
- }.execute();
- }
+ return false;
+ }
+
+ @Override
+ public void updateResult(Entry result) {
+ result.setStarred(starred);
+ }
+ }.execute();
}
}
@@ -769,18 +797,161 @@ public class CachedMusicService implements MusicService {
}
@Override
- public List<Bookmark> getBookmarks(boolean refresh, Context context, ProgressListener progressListener) throws Exception {
- return musicService.getBookmarks(refresh, context, progressListener);
+ public MusicDirectory getBookmarks(boolean refresh, Context context, ProgressListener progressListener) throws Exception {
+ MusicDirectory bookmarks = musicService.getBookmarks(refresh, context, progressListener);
+
+ MusicDirectory oldBookmarks = FileUtil.deserialize(context, "bookmarks", MusicDirectory.class);
+ if(oldBookmarks != null) {
+ final List<Entry> oldList = oldBookmarks.getChildren();
+ final List<Entry> newList = new ArrayList<Entry>();
+ newList.addAll(bookmarks.getChildren());
+
+ for(Iterator<Entry> it = oldList.iterator(); it.hasNext(); ) {
+ Entry oldEntry = it.next();
+ // Remove entries from newList
+ int position = newList.indexOf(oldEntry);
+ if(position != -1) {
+ Entry newEntry = newList.get(position);
+ if(newEntry.getBookmark().getPosition() == oldEntry.getBookmark().getPosition()) {
+ newList.remove(position);
+ }
+
+ // Remove from old regardless of whether position is wrong
+ it.remove();
+ }
+ }
+
+ // Remove bookmarks from thinsg still in old list
+ setBookmarkCache(context, oldList, true);
+ // Add new bookmarks for things in new list
+ setBookmarkCache(context, newList, false);
+
+ if(oldList.size() > 0 || newList.size() > 0) {
+ new PlaylistDirectoryUpdater(context) {
+ @Override
+ public boolean checkResult(Entry check) {
+ for(Entry entry: oldList) {
+ if(entry.getId().equals(check.getId()) && check.getBookmark() != null) {
+ check.setBookmark(null);
+ return true;
+ }
+ }
+ for(Entry entry: newList) {
+ if(entry.getId().equals(check.getId())) {
+ int newPosition = entry.getBookmark().getPosition();
+ if(check.getBookmark() == null || check.getBookmark().getPosition() != newPosition) {
+ setBookmarkCache(check, newPosition);
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ @Override
+ public void updateResult(Entry result) {
+
+ }
+ }.execute();
+ }
+ }
+ FileUtil.serialize(context, bookmarks, "bookmarks");
+
+ return bookmarks;
}
@Override
- public void createBookmark(String id, int position, String comment, Context context, ProgressListener progressListener) throws Exception {
- musicService.createBookmark(id, position, comment, context, progressListener);
+ public void createBookmark(String id, String parent, int position, String comment, Context context, ProgressListener progressListener) throws Exception {
+ musicService.createBookmark(id, null, position, comment, context, progressListener);
+ // Add to directory cache
+ setBookmarkCache(context, id, parent, position);
+ // Add to playlist cache
+ setBookmarkCache(context, id, position);
}
@Override
- public void deleteBookmark(String id, Context context, ProgressListener progressListener) throws Exception {
- musicService.deleteBookmark(id, context, progressListener);
+ public void deleteBookmark(String id, String parent, Context context, ProgressListener progressListener) throws Exception {
+ musicService.deleteBookmark(id, null, context, progressListener);
+ // Delete from directory cache
+ setBookmarkCache(context, id, parent, -1);
+ // Delete from playlist cache
+ setBookmarkCache(context, id, -1);
+ }
+
+ private void setBookmarkCache(Context context, List<Entry> entries, boolean remove) {
+ for(final Entry entry: entries) {
+ if(remove) {
+ setBookmarkCache(context, entry.getId(), Util.getParentFromEntry(context, entry), -1);
+ } else {
+ setBookmarkCache(context, entry.getId(), Util.getParentFromEntry(context, entry), entry.getBookmark().getPosition());
+ }
+ }
+ }
+ private void setBookmarkCache(Context context, final String id, final String parent, final int position) {
+ String cacheName;
+ if(isTagBrowsing) {
+ cacheName = "album";
+ } else {
+ cacheName = "directory";
+ }
+
+ // Update the parent directory with bookmark data
+ new MusicDirectoryUpdater(context, cacheName, parent) {
+ @Override
+ public boolean checkResult(Entry check) {
+ return shouldBookmarkUpdate(check, id, position);
+ }
+
+ @Override
+ public void updateResult(List<Entry> objects, Entry result) {
+ setBookmarkCache(result, position);
+ }
+ }.execute();
+ }
+ private void setBookmarkCache(Context context, final String id, final int position) {
+ // Update playlists with bookmark data
+ new PlaylistDirectoryUpdater(context) {
+ @Override
+ public boolean checkResult(Entry check) {
+ return shouldBookmarkUpdate(check, id, position);
+ }
+
+ @Override
+ public void updateResult(Entry result) {
+ setBookmarkCache(result, position);
+ }
+ }.execute();
+ }
+
+ private boolean shouldBookmarkUpdate(Entry check, String id, int position) {
+ if(id.equals(check.getId())) {
+ if(position == -1 && check.getBookmark() != null) {
+ return true;
+ } else if(position >= 0 && (check.getBookmark() == null || check.getBookmark().getPosition() != position)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ private void setBookmarkCache(Entry result, int position) {
+ // If position == -1, then it is a delete
+ if(result.getBookmark() != null && position == -1) {
+ result.setBookmark(null);
+ } else if(position >= 0) {
+ Bookmark bookmark = result.getBookmark();
+
+ // Create one if empty
+ if(bookmark == null) {
+ bookmark = new Bookmark();
+ result.setBookmark(bookmark);
+ }
+
+ // Update bookmark position no matter what
+ bookmark.setPosition(position);
+ }
}
@Override
@@ -901,17 +1072,36 @@ public class CachedMusicService implements MusicService {
return name + "-" + s.hashCode() + ".ser";
}
+ private void removeDuplicates(List<Entry> oldList, List<Entry> newList) {
+ for(Iterator<Entry> it = oldList.iterator(); it.hasNext(); ) {
+ // Remove entries from newList
+ if(newList.remove(it.next())) {
+ // If it was removed, then remove it from old list as well
+ it.remove();
+ }
+ }
+ }
+
private abstract class SerializeUpdater<T> {
final Context context;
final String cacheName;
+ final boolean singleUpdate;
public SerializeUpdater(Context context, String cacheName) {
+ this(context, cacheName, true);
+ }
+ public SerializeUpdater(Context context, String cacheName, boolean singleUpdate) {
this.context = context;
this.cacheName = getCacheName(context, cacheName);
+ this.singleUpdate = singleUpdate;
+ }
+ public SerializeUpdater(Context context, String cacheName, String id) {
+ this(context, cacheName, id, true);
}
- public SerializeUpdater(Context context, String cacheName, String id) {
+ public SerializeUpdater(Context context, String cacheName, String id, boolean singleUpdate) {
this.context = context;
this.cacheName = getCacheName(context, cacheName, id);
+ this.singleUpdate = singleUpdate;
}
public ArrayList<T> getArrayList() {
@@ -928,17 +1118,23 @@ public class CachedMusicService implements MusicService {
// Only execute if something to check against
if(objects != null) {
- T object = null;
+ List<T> results = new ArrayList<T>();
for(T check: objects) {
if(checkResult(check)) {
- object = check;
- break;
+ results.add(check);
+ if(singleUpdate) {
+ break;
+ }
}
}
- // Only reserialize if a match was found
- if(object != null) {
- updateResult(objects, object);
+ // Iterate through and update each object matched
+ for(T result: results) {
+ updateResult(objects, result);
+ }
+
+ // Only reserialize if at least one match was found
+ if(results.size() > 0) {
save(objects);
}
}
@@ -974,7 +1170,10 @@ public class CachedMusicService implements MusicService {
private MusicDirectory musicDirectory;
public MusicDirectoryUpdater(Context context, String cacheName, String id) {
- super(context, cacheName, id);
+ super(context, cacheName, id, true);
+ }
+ public MusicDirectoryUpdater(Context context, String cacheName, String id, boolean singleUpdate) {
+ super(context, cacheName, id, singleUpdate);
}
@Override
@@ -991,6 +1190,38 @@ public class CachedMusicService implements MusicService {
FileUtil.serialize(context, musicDirectory, cacheName);
}
}
+ private abstract class PlaylistDirectoryUpdater {
+ Context context;
+
+ public PlaylistDirectoryUpdater(Context context) {
+ this.context = context;
+ }
+
+ public abstract boolean checkResult(Entry check);
+ public abstract void updateResult(Entry result);
+
+ public void execute() {
+ List<Playlist> playlists = FileUtil.deserialize(context, getCacheName(context, "playlist"), ArrayList.class);
+ if(playlists == null) {
+ // No playlist list cache, nothing to update!
+ return;
+ }
+
+ for(Playlist playlist: playlists) {
+ new MusicDirectoryUpdater(context, "playlist", playlist.getId(), false) {
+ @Override
+ public boolean checkResult(Entry check) {
+ return PlaylistDirectoryUpdater.this.checkResult(check);
+ }
+
+ @Override
+ public void updateResult(List<Entry> objects, Entry result) {
+ PlaylistDirectoryUpdater.this.updateResult(result);
+ }
+ }.execute();
+ }
+ }
+ }
private abstract class IndexesUpdater extends SerializeUpdater<Artist> {
Indexes indexes;
diff --git a/src/github/daneren2005/dsub/service/DownloadService.java b/src/github/daneren2005/dsub/service/DownloadService.java
index b96d76bd..54df07ac 100644
--- a/src/github/daneren2005/dsub/service/DownloadService.java
+++ b/src/github/daneren2005/dsub/service/DownloadService.java
@@ -49,6 +49,7 @@ import github.daneren2005.dsub.util.SimpleServiceBinder;
import github.daneren2005.dsub.util.SyncUtil;
import github.daneren2005.dsub.util.Util;
import github.daneren2005.dsub.util.compat.RemoteControlClientHelper;
+import github.daneren2005.dsub.view.UpdateView;
import github.daneren2005.serverproxy.BufferProxy;
import java.io.File;
@@ -94,6 +95,7 @@ public class DownloadService extends Service {
public static final String START_PLAY = "github.daneren2005.dsub.START_PLAYING";
public static final int FAST_FORWARD = 30000;
public static final int REWIND = 10000;
+ private static final double DELETE_CUTOFF = 0.90;
private RemoteControlClientHelper mRemoteControl;
@@ -294,17 +296,10 @@ public class DownloadService extends Service {
return binder;
}
- public synchronized void download(Bookmark bookmark) {
- clear();
- DownloadFile downloadFile = new DownloadFile(this, bookmark.getEntry(), false);
- downloadList.add(downloadFile);
- revision++;
- updateJukeboxPlaylist();
- play(0, true, bookmark.getPosition());
- lifecycleSupport.serializeDownloadQueue();
- }
-
public synchronized void download(List<MusicDirectory.Entry> songs, boolean save, boolean autoplay, boolean playNext, boolean shuffle) {
+ download(songs, save, autoplay, playNext, shuffle, 0, 0);
+ }
+ public synchronized void download(List<MusicDirectory.Entry> songs, boolean save, boolean autoplay, boolean playNext, boolean shuffle, int start, int position) {
setShufflePlayEnabled(false);
int offset = 1;
@@ -343,7 +338,7 @@ public class DownloadService extends Service {
}
if (autoplay) {
- play(0);
+ play(start, true, position);
} else {
if (currentPlaying == null) {
currentPlaying = downloadList.get(0);
@@ -564,7 +559,7 @@ public class DownloadService extends Service {
lifecycleSupport.post(new Runnable() {
@Override
public void run() {
- if(online) {
+ if (online) {
checkDownloads();
} else {
clearIncomplete();
@@ -582,12 +577,9 @@ public class DownloadService extends Service {
public synchronized void clear(boolean serialize) {
// Delete podcast if fully listened to
+ boolean cutoff = isPastCutoff();
if(currentPlaying != null && currentPlaying.getSong() instanceof PodcastEpisode) {
- int duration = getPlayerDuration();
-
- // Make sure > 90% of the way through
- int cutoffPoint = (int)(duration * 0.90);
- if(duration > 0 && cachedPosition > cutoffPoint) {
+ if(cutoff) {
currentPlaying.delete();
}
}
@@ -595,6 +587,14 @@ public class DownloadService extends Service {
podcast.delete();
}
toDelete.clear();
+
+ // Clear bookmarks from current playing if past a certain point
+ if(cutoff) {
+ clearCurrentBookmark(true);
+ } else {
+ // Check if we should be adding a new bookmark here
+ checkAddBookmark();
+ }
reset();
downloadList.clear();
@@ -905,14 +905,11 @@ public class DownloadService extends Service {
// Delete podcast if fully listened to
if(currentPlaying != null && currentPlaying.getSong() instanceof PodcastEpisode) {
- int duration = getPlayerDuration();
-
- // Make sure > 90% of the way through
- int cutoffPoint = (int)(duration * 0.90);
- if(duration > 0 && cachedPosition > cutoffPoint) {
+ if(isPastCutoff()) {
toDelete.add(currentPlaying);
}
}
+ clearCurrentBookmark();
int index = getCurrentPlayingIndex();
int nextPlayingIndex = getNextPlayingIndex();
@@ -1381,7 +1378,7 @@ public class DownloadService extends Service {
mediaPlayer.setOnBufferingUpdateListener(new MediaPlayer.OnBufferingUpdateListener() {
public void onBufferingUpdate(MediaPlayer mp, int percent) {
Log.i(TAG, "Buffered " + percent + "%");
- if(percent == 100) {
+ if (percent == 100) {
mediaPlayer.setOnBufferingUpdateListener(null);
}
}
@@ -1510,6 +1507,7 @@ public class DownloadService extends Service {
if(downloadFile.getSong() instanceof PodcastEpisode) {
toDelete.add(downloadFile);
}
+ clearCurrentBookmark(downloadFile.getSong(), true);
} else {
// If file is not completely downloaded, restart the playback from the current position.
synchronized (DownloadService.this) {
@@ -1787,6 +1785,104 @@ public class DownloadService extends Service {
}
}
}
+
+ private boolean isPastCutoff() {
+ int duration = getPlayerDuration();
+ int cutoffPoint = (int) (duration * DELETE_CUTOFF);
+ return duration > 0 && cachedPosition > cutoffPoint;
+ }
+
+ private void clearCurrentBookmark() {
+ clearCurrentBookmark(false);
+ }
+ private void clearCurrentBookmark(boolean delete) {
+ // If current is null, nothing to do
+ if(currentPlaying == null) {
+ return;
+ }
+
+ clearCurrentBookmark(currentPlaying.getSong(), delete);
+ }
+ private void clearCurrentBookmark(final MusicDirectory.Entry entry, boolean delete) {
+ // If no bookmark, move on
+ if(entry.getBookmark() == null) {
+ return;
+ }
+
+ // If delete is not specified, check position
+ if(!delete) {
+ delete = isPastCutoff();
+ }
+
+ // If supposed to delete
+ if(delete) {
+ new SilentBackgroundTask<Void>(this) {
+ @Override
+ public Void doInBackground() throws Throwable {
+ MusicService musicService = MusicServiceFactory.getMusicService(DownloadService.this);
+ musicService.deleteBookmark(entry.getId(), Util.getParentFromEntry(DownloadService.this, entry), DownloadService.this, null);
+
+ entry.setBookmark(null);
+ MusicDirectory.Entry found = UpdateView.findEntry(entry);
+ if(found != null) {
+ found.setBookmark(null);
+ }
+ return null;
+ }
+
+ @Override
+ public void error(Throwable error) {
+ Log.e(TAG, "Failed to delete bookmark", error);
+
+ String msg;
+ if(error instanceof OfflineException || error instanceof ServerTooOldException) {
+ msg = getErrorMessage(error);
+ } else {
+ msg = DownloadService.this.getResources().getString(R.string.bookmark_deleted_error, entry.getTitle()) + " " + getErrorMessage(error);
+ }
+
+ Util.toast(DownloadService.this, msg, false);
+ }
+ }.execute();
+ }
+ }
+
+ private void checkAddBookmark() {
+ // Don't do anything if no current playing
+ if(currentPlaying == null) {
+ return;
+ }
+
+ final MusicDirectory.Entry entry = currentPlaying.getSong();
+ int duration = getPlayerDuration();
+
+ // If song is podcast or long go ahead and auto add a bookmark
+ if(entry instanceof PodcastEpisode || duration > (10L * 60L * 1000L)) {
+ final Context context = this;
+ final int position = getPlayerPosition();
+
+ // Don't bother when at beginning
+ if(position < 5000L) {
+ return;
+ }
+
+ new SilentBackgroundTask<Void>(context) {
+ @Override
+ public Void doInBackground() throws Throwable {
+ MusicService musicService = MusicServiceFactory.getMusicService(context);
+ musicService.createBookmark(entry.getId(), Util.getParentFromEntry(context, entry), position, "Auto created by DSub", context, null);
+
+ entry.setBookmark(new Bookmark(position));
+ MusicDirectory.Entry found = UpdateView.findEntry(entry);
+ if(found != null) {
+ found.setBookmark(new Bookmark(position));
+ }
+
+ return null;
+ }
+ }.execute();
+ }
+ }
private class BufferTask extends SilentBackgroundTask<Void> {
private final DownloadFile downloadFile;
diff --git a/src/github/daneren2005/dsub/service/MusicService.java b/src/github/daneren2005/dsub/service/MusicService.java
index f0751230..d70f1c46 100644
--- a/src/github/daneren2005/dsub/service/MusicService.java
+++ b/src/github/daneren2005/dsub/service/MusicService.java
@@ -154,11 +154,11 @@ public interface MusicService {
void setRating(String id, int rating, Context context, ProgressListener progressListener) throws Exception;
- List<Bookmark> getBookmarks(boolean refresh, Context context, ProgressListener progressListener) throws Exception;
+ MusicDirectory getBookmarks(boolean refresh, Context context, ProgressListener progressListener) throws Exception;
- void createBookmark(String id, int position, String comment, Context context, ProgressListener progressListener) throws Exception;
+ void createBookmark(String id, String parent, int position, String comment, Context context, ProgressListener progressListener) throws Exception;
- void deleteBookmark(String id, Context context, ProgressListener progressListener) throws Exception;
+ void deleteBookmark(String id, String parent, Context context, ProgressListener progressListener) throws Exception;
User getUser(boolean refresh, String username, Context context, ProgressListener progressListener) throws Exception;
diff --git a/src/github/daneren2005/dsub/service/OfflineMusicService.java b/src/github/daneren2005/dsub/service/OfflineMusicService.java
index 329bf5cd..8aeba2b4 100644
--- a/src/github/daneren2005/dsub/service/OfflineMusicService.java
+++ b/src/github/daneren2005/dsub/service/OfflineMusicService.java
@@ -37,7 +37,6 @@ import android.util.Log;
import org.apache.http.HttpResponse;
import github.daneren2005.dsub.domain.Artist;
-import github.daneren2005.dsub.domain.Bookmark;
import github.daneren2005.dsub.domain.ChatMessage;
import github.daneren2005.dsub.domain.Genre;
import github.daneren2005.dsub.domain.Indexes;
@@ -743,17 +742,17 @@ public class OfflineMusicService implements MusicService {
}
@Override
- public List<Bookmark> getBookmarks(boolean refresh, Context context, ProgressListener progressListener) throws Exception {
+ public MusicDirectory getBookmarks(boolean refresh, Context context, ProgressListener progressListener) throws Exception {
throw new OfflineException(ERRORMSG);
}
@Override
- public void createBookmark(String id, int position, String comment, Context context, ProgressListener progressListener) throws Exception {
+ public void createBookmark(String id, String parent, int position, String comment, Context context, ProgressListener progressListener) throws Exception {
throw new OfflineException(ERRORMSG);
}
@Override
- public void deleteBookmark(String id, Context context, ProgressListener progressListener) throws Exception {
+ public void deleteBookmark(String id, String parent, Context context, ProgressListener progressListener) throws Exception {
throw new OfflineException(ERRORMSG);
}
diff --git a/src/github/daneren2005/dsub/service/RESTMusicService.java b/src/github/daneren2005/dsub/service/RESTMusicService.java
index 52d0f877..025d07c5 100644
--- a/src/github/daneren2005/dsub/service/RESTMusicService.java
+++ b/src/github/daneren2005/dsub/service/RESTMusicService.java
@@ -1115,7 +1115,7 @@ public class RESTMusicService implements MusicService {
}
@Override
- public List<Bookmark> getBookmarks(boolean refresh, Context context, ProgressListener progressListener) throws Exception {
+ public MusicDirectory getBookmarks(boolean refresh, Context context, ProgressListener progressListener) throws Exception {
checkServerVersion(context, "1.9", "Bookmarks not supported.");
Reader reader = getReader(context, progressListener, "getBookmarks", null);
@@ -1127,7 +1127,7 @@ public class RESTMusicService implements MusicService {
}
@Override
- public void createBookmark(String id, int position, String comment, Context context, ProgressListener progressListener) throws Exception {
+ public void createBookmark(String id, String parent, int position, String comment, Context context, ProgressListener progressListener) throws Exception {
checkServerVersion(context, "1.9", "Creating bookmarks not supported.");
Reader reader = getReader(context, progressListener, "createBookmark", null, Arrays.asList("id", "position", "comment"), Arrays.<Object>asList(id, position, comment));
@@ -1139,7 +1139,7 @@ public class RESTMusicService implements MusicService {
}
@Override
- public void deleteBookmark(String id, Context context, ProgressListener progressListener) throws Exception {
+ public void deleteBookmark(String id, String parent, Context context, ProgressListener progressListener) throws Exception {
checkServerVersion(context, "1.9", "Deleting bookmarks not supported.");
Reader reader = getReader(context, progressListener, "deleteBookmark", null, Arrays.asList("id"), Arrays.<Object>asList(id));
diff --git a/src/github/daneren2005/dsub/service/parser/BookmarkParser.java b/src/github/daneren2005/dsub/service/parser/BookmarkParser.java
index 68dc3898..332172a3 100644
--- a/src/github/daneren2005/dsub/service/parser/BookmarkParser.java
+++ b/src/github/daneren2005/dsub/service/parser/BookmarkParser.java
@@ -36,10 +36,10 @@ public class BookmarkParser extends MusicDirectoryEntryParser {
super(context, instance);
}
- public List<Bookmark> parse(Reader reader, ProgressListener progressListener) throws Exception {
+ public MusicDirectory parse(Reader reader, ProgressListener progressListener) throws Exception {
init(reader);
- List<Bookmark> bookmarks = new ArrayList<Bookmark>();
+ List<MusicDirectory.Entry> bookmarks = new ArrayList<MusicDirectory.Entry>();
Bookmark bookmark = null;
int eventType;
@@ -58,9 +58,12 @@ public class BookmarkParser extends MusicDirectoryEntryParser {
bookmark.setUsername(get("username"));
} else if ("entry".equals(name)) {
MusicDirectory.Entry entry = parseEntry(null);
- entry.setTrack(null);
- bookmark.setEntry(entry);
- bookmarks.add(bookmark);
+ // Work around for bookmarks showing entry with a track when podcast listings don't
+ if("podcast".equals(get("type"))) {
+ entry.setTrack(null);
+ }
+ entry.setBookmark(bookmark);
+ bookmarks.add(entry);
} else if ("error".equals(name)) {
handleError();
}
@@ -69,6 +72,6 @@ public class BookmarkParser extends MusicDirectoryEntryParser {
validate();
- return bookmarks;
+ return new MusicDirectory(bookmarks);
}
}
diff --git a/src/github/daneren2005/dsub/service/parser/MusicDirectoryEntryParser.java b/src/github/daneren2005/dsub/service/parser/MusicDirectoryEntryParser.java
index a9bafd12..67effc24 100644
--- a/src/github/daneren2005/dsub/service/parser/MusicDirectoryEntryParser.java
+++ b/src/github/daneren2005/dsub/service/parser/MusicDirectoryEntryParser.java
@@ -19,6 +19,8 @@
package github.daneren2005.dsub.service.parser;
import android.content.Context;
+
+import github.daneren2005.dsub.domain.Bookmark;
import github.daneren2005.dsub.domain.MusicDirectory;
/**
@@ -59,6 +61,11 @@ public class MusicDirectoryEntryParser extends AbstractParser {
entry.setPath(get("path"));
entry.setVideo(getBoolean("isVideo"));
entry.setDiscNumber(getInteger("discNumber"));
+
+ Integer bookmark = getInteger("bookmarkPosition");
+ if(bookmark != null) {
+ entry.setBookmark(new Bookmark(bookmark));
+ }
} else if(!"".equals(artist)) {
entry.setPath(artist + "/" + entry.getTitle());
}
diff --git a/src/github/daneren2005/dsub/service/parser/PodcastEntryParser.java b/src/github/daneren2005/dsub/service/parser/PodcastEntryParser.java
index 0ccac3a3..afd861f0 100644
--- a/src/github/daneren2005/dsub/service/parser/PodcastEntryParser.java
+++ b/src/github/daneren2005/dsub/service/parser/PodcastEntryParser.java
@@ -20,6 +20,7 @@ package github.daneren2005.dsub.service.parser;
import android.content.Context;
import github.daneren2005.dsub.R;
+import github.daneren2005.dsub.domain.Bookmark;
import github.daneren2005.dsub.domain.MusicDirectory;
import github.daneren2005.dsub.domain.PodcastEpisode;
import github.daneren2005.dsub.util.FileUtil;
@@ -87,6 +88,11 @@ public class PodcastEntryParser extends AbstractParser {
} else if(episode.getPath().indexOf("Podcasts/") == 0) {
episode.setPath(episode.getPath().substring("Podcasts/".length()));
}
+
+ Integer bookmark = getInteger("bookmarkPosition");
+ if(bookmark != null) {
+ episode.setBookmark(new Bookmark(bookmark));
+ }
if(episode.getId() == null) {
episode.setId(String.valueOf(bogusId));
diff --git a/src/github/daneren2005/dsub/util/FileUtil.java b/src/github/daneren2005/dsub/util/FileUtil.java
index 304541b6..6c116dce 100644
--- a/src/github/daneren2005/dsub/util/FileUtil.java
+++ b/src/github/daneren2005/dsub/util/FileUtil.java
@@ -619,7 +619,7 @@ public class FileUtil {
Log.w(TAG, "No serialization for object from " + fileName);
return null;
} catch (Throwable x) {
- Log.w(TAG, "Failed to deserialize object from " + fileName, x);
+ Log.w(TAG, "Failed to deserialize object from " + fileName);
return null;
} finally {
Util.close(in);
@@ -659,7 +659,7 @@ public class FileUtil {
Log.w(TAG, "No serialization compressed for object from " + fileName);
return null;
} catch (Throwable x) {
- Log.w(TAG, "Failed to deserialize compressed object from " + fileName, x);
+ Log.w(TAG, "Failed to deserialize compressed object from " + fileName);
return null;
} finally {
Util.close(in);
diff --git a/src/github/daneren2005/dsub/util/Util.java b/src/github/daneren2005/dsub/util/Util.java
index 1ea1a3be..258e4eb8 100644
--- a/src/github/daneren2005/dsub/util/Util.java
+++ b/src/github/daneren2005/dsub/util/Util.java
@@ -385,6 +385,20 @@ public final class Util {
return prefs.getBoolean(Constants.PREFERENCES_KEY_BROWSE_TAGS + instance, false);
}
+ public static String getParentFromEntry(Context context, MusicDirectory.Entry entry) {
+ if(Util.isTagBrowsing(context)) {
+ if(!entry.isDirectory()) {
+ return entry.getAlbumId();
+ } else if(entry.isAlbum()) {
+ return entry.getArtistId();
+ } else {
+ return null;
+ }
+ } else {
+ return entry.getParent();
+ }
+ }
+
public static boolean isOpenToLibrary(Context context) {
SharedPreferences prefs = getPreferences(context);
return prefs.getBoolean(Constants.PREFERENCES_KEY_OPEN_TO_LIBRARY, false);
diff --git a/src/github/daneren2005/dsub/view/BookmarkAdapter.java b/src/github/daneren2005/dsub/view/BookmarkAdapter.java
index b0541397..ea585744 100644
--- a/src/github/daneren2005/dsub/view/BookmarkAdapter.java
+++ b/src/github/daneren2005/dsub/view/BookmarkAdapter.java
@@ -32,18 +32,19 @@ import github.daneren2005.dsub.domain.Bookmark;
import github.daneren2005.dsub.domain.MusicDirectory;
import github.daneren2005.dsub.util.Util;
-public class BookmarkAdapter extends ArrayAdapter<Bookmark> {
+public class BookmarkAdapter extends ArrayAdapter<MusicDirectory.Entry> {
private final static String TAG = BookmarkAdapter.class.getSimpleName();
private Context activity;
- public BookmarkAdapter(Context activity, List<Bookmark> bookmarks) {
+ public BookmarkAdapter(Context activity, List<MusicDirectory.Entry> bookmarks) {
super(activity, android.R.layout.simple_list_item_1, bookmarks);
this.activity = activity;
}
public View getView(int position, View convertView, ViewGroup parent) {
- Bookmark bookmark = getItem(position);
- MusicDirectory.Entry entry = bookmark.getEntry();
+ MusicDirectory.Entry entry = getItem(position);
+ Bookmark bookmark = entry.getBookmark();
+
SongView view;
if (convertView != null) {
view = (SongView) convertView;