diff options
author | Scott Jackson <daneren2005@gmail.com> | 2014-08-29 16:52:02 -0700 |
---|---|---|
committer | Scott Jackson <daneren2005@gmail.com> | 2014-08-29 16:52:02 -0700 |
commit | 707c4682e48ca7e36ca34dceb0c271f783ab273a (patch) | |
tree | 387ee4053424db65d9204354cd6323cbebbc89b0 | |
parent | 1739dec2588fb69ac9f36eaf8b87133c3c48dd02 (diff) | |
parent | c334900517c2b015938718e9ac85f8762f74ca9f (diff) | |
download | dsub-707c4682e48ca7e36ca34dceb0c271f783ab273a.tar.gz dsub-707c4682e48ca7e36ca34dceb0c271f783ab273a.tar.bz2 dsub-707c4682e48ca7e36ca34dceb0c271f783ab273a.zip |
Merge branch 'master' of https://github.com/daneren2005/Subsonic into VideoDownload
48 files changed, 1307 insertions, 248 deletions
diff --git a/AndroidManifest.xml b/AndroidManifest.xml index f48ad1c5..282936a0 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -2,8 +2,8 @@ <manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="github.daneren2005.dsub"
android:installLocation="internalOnly"
- android:versionCode="120"
- android:versionName="4.7.4">
+ android:versionCode="121"
+ android:versionName="4.7.5">
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.READ_PHONE_STATE"/>
diff --git a/DSub Featured.png b/DSub Featured.png Binary files differnew file mode 100644 index 00000000..c54fe16c --- /dev/null +++ b/DSub Featured.png diff --git a/DragSortListView b/DragSortListView -Subproject 55d8788fef9580e2f18ac789b76d8191b1d1d50 +Subproject c366792f270e6919fa75503347c30ab62919c5f diff --git a/ServerProxy b/ServerProxy -Subproject 940c61d09a575b3402a21bcb3583189e72a209a +Subproject a0bfee5ce4c9ad8c480b4d09e027bf48a71b75e diff --git a/res/drawable-hdpi/ic_number_border.png b/res/drawable-hdpi/ic_number_border.png Binary files differnew file mode 100644 index 00000000..d05aa7c2 --- /dev/null +++ b/res/drawable-hdpi/ic_number_border.png diff --git a/res/drawable-mdpi/ic_number_border.png b/res/drawable-mdpi/ic_number_border.png Binary files differnew file mode 100644 index 00000000..212fabce --- /dev/null +++ b/res/drawable-mdpi/ic_number_border.png diff --git a/res/drawable-xhdpi/ic_number_border.png b/res/drawable-xhdpi/ic_number_border.png Binary files differnew file mode 100644 index 00000000..1b370fbd --- /dev/null +++ b/res/drawable-xhdpi/ic_number_border.png diff --git a/res/drawable-xxhdpi/ic_number_border.png b/res/drawable-xxhdpi/ic_number_border.png Binary files differnew file mode 100644 index 00000000..caf4ca23 --- /dev/null +++ b/res/drawable-xxhdpi/ic_number_border.png diff --git a/res/layout/abstract_list_fragment.xml b/res/layout/abstract_list_fragment.xml index 4440c526..8d586105 100644 --- a/res/layout/abstract_list_fragment.xml +++ b/res/layout/abstract_list_fragment.xml @@ -15,13 +15,13 @@ android:layout_height="1px"
android:background="@color/dividerColor"/>
- <include layout="@layout/tab_progress" />
-
<ListView
android:id="@+id/fragment_list"
android:layout_width="fill_parent"
android:layout_height="0dip"
android:layout_weight="1.0"
android:fastScrollEnabled="true"/>
+
+ <include layout="@layout/tab_progress" />
</LinearLayout>
</android.support.v4.widget.SwipeRefreshLayout>
\ No newline at end of file diff --git a/res/layout/basic_count_item.xml b/res/layout/basic_count_item.xml new file mode 100644 index 00000000..36a0e75d --- /dev/null +++ b/res/layout/basic_count_item.xml @@ -0,0 +1,35 @@ +<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:background="@android:color/transparent"
+ android:minHeight="50dip">
+
+ <TextView
+ android:id="@+id/basic_count_name"
+ android:layout_width="wrap_content"
+ android:layout_height="fill_parent"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:gravity="left|center_vertical"
+ android:paddingLeft="6dip"
+ android:paddingRight="6dip"
+ android:background="@android:color/transparent"
+ android:text="Text"/>
+
+ <TextView
+ android:id="@+id/basic_count_count"
+ android:layout_width="32dp"
+ android:layout_height="32dp"
+ android:layout_marginRight="12dp"
+ android:background="@drawable/ic_number_border"
+ android:focusable="false"
+ android:paddingRight="10dp"
+ android:layout_marginLeft="20px"
+ android:layout_marginBottom="4px"
+ android:text="99"
+ android:textAppearance="?android:attr/textAppearanceSmallPopupMenu"
+ android:textSize="11sp"
+ android:gravity="right|center_vertical"
+ android:layout_gravity="center_vertical"
+ android:visibility="gone"/>
+</LinearLayout>
\ No newline at end of file diff --git a/res/layout/main_buttons.xml b/res/layout/main_buttons.xml index d83632c1..3c190cca 100644 --- a/res/layout/main_buttons.xml +++ b/res/layout/main_buttons.xml @@ -60,10 +60,36 @@ android:text="@string/main.albums_title"
style="@style/MainAlbumButtonLabel"/>
- <TextView
+ <LinearLayout
android:id="@+id/main_albums_newest"
- android:text="@string/main.albums_newest"
- style="@style/MainAlbumButton"/>
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:minHeight="46dip">
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="fill_parent"
+ android:text="@string/main.albums_newest"
+ style="@style/MainAlbumButton"/>
+
+ <TextView
+ android:id="@+id/main_albums_recent_count"
+ android:layout_width="32dp"
+ android:layout_height="32dp"
+ android:layout_marginRight="12dp"
+ android:background="@drawable/ic_number_border"
+ android:focusable="false"
+ android:paddingRight="10dp"
+ android:layout_marginLeft="20px"
+ android:layout_marginBottom="4px"
+ android:text="99"
+ android:textAppearance="?android:attr/textAppearanceSmallPopupMenu"
+ android:textSize="11sp"
+ android:gravity="right|center_vertical"
+ android:layout_gravity="center_vertical"
+ android:visibility="gone"/>
+ </LinearLayout>
+
<TextView
android:id="@+id/main_albums_recent"
android:text="@string/main.albums_recent"
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-de/strings.xml b/res/values-de/strings.xml index d2cd608e..a9362206 100644 --- a/res/values-de/strings.xml +++ b/res/values-de/strings.xml @@ -237,6 +237,10 @@ \nErzeugt: %3$s
\nZuletzt aktualisiert: %4$s
\nKommentar: %5$s</string>
+ <string name="bookmark.resume_title">Wiedergabe fortsetzen?</string>
+ <string name="bookmark.resume">\'%1$s\' fortsetzen bei %2$s</string>
+ <string name="bookmark.action_resume">Fortsetzen</string>
+ <string name="bookmark.action_start_over">Neu beginnen</string>
<string name="song_details.error">Fehler</string>
<string name="song_details.skipped">Überspringen</string>
diff --git a/res/values-hu/strings.xml b/res/values-hu/strings.xml index 626330ee..cd8f9dfd 100644 --- a/res/values-hu/strings.xml +++ b/res/values-hu/strings.xml @@ -27,7 +27,7 @@ <string name="common.warning">Figyelem!</string>
<string name="button_bar.home">Főoldal</string>
- <string name="button_bar.browse">Médiakönyvtár</string>
+ <string name="button_bar.browse">Médiatár</string>
<string name="button_bar.search">Keresés</string>
<string name="button_bar.playlists">Lejátszási listák</string>
<string name="button_bar.now_playing">Várólista</string>
@@ -73,6 +73,7 @@ <string name="main.albums_year">Évtizedek</string>
<string name="main.songs_genres">@string/main.albums_genres</string>
<string name="main.back_confirm">Nyomja meg még egyszer a kilépéshez!</string>
+ <string name="main.scan_complete">A médiatár frissítése befejeződött a kiszolgálón!</string>
<string name="menu.search">Keresés</string>
<string name="menu.shuffle">Lejátszás kevert sorrendben</string>
@@ -101,6 +102,7 @@ <string name="menu.cast">Továbbítás eszközhöz</string>
<string name="menu.faq">FAQ</string>
<string name="menu.add_user">Felhasználó hozzáadása</string>
+ <string name="menu.rescan">Médiatár frissítése a kiszolgálón</string>
<string name="playlist.label">Lejátszási listák</string>
<string name="playlist.update_info">Szerkesztés</string>
@@ -122,7 +124,7 @@ <string name="progress.wait">Kérem várjon...</string>
- <string name="music_library.label">Médiakönyvtár</string>
+ <string name="music_library.label">Médiatár</string>
<string name="music_library.label_offline">Kapcsolat nélküli médiák</string>
<string name="select_album.select">Összes jelölése be/ki</string>
@@ -212,9 +214,9 @@ </string>
<string name="download.failed_to_load">A beolvasás sikertelen!</string>
- <string name="sync.new_podcasts">Új podcastok</string>
- <string name="sync.new_playlists">Új lejátszási listák</string>
- <string name="sync.new_albums">Új albumok</string>
+ <string name="sync.new_podcasts">Új podcastok: \"%s\"</string>
+ <string name="sync.new_playlists">Új lejátszási listák: \"%s\"</string>
+ <string name="sync.new_albums">Új albumok: \"%s\"</string>
<string name="sync.new_starred">Új csillagozott dalok</string>
<string name="starring_content_starred">\"%s\" csillagozás be</string>
@@ -236,6 +238,10 @@ \nLétrehozva: %3$s
\nUtolsó módosítás: %4$s
\nMegjegyzés: %5$s</string>
+ <string name="bookmark.resume_title">Folytatja a lejátszást?</string>
+ <string name="bookmark.resume">\'%1$s\' folytatása innen: \'%2$s\'</string>
+ <string name="bookmark.action_resume">Folytatás</string>
+ <string name="bookmark.action_start_over">Kezdés</string>
<string name="song_details.error">Hiba</string>
<string name="song_details.skipped">Átlépve</string>
@@ -286,8 +292,8 @@ <string name="settings.track_summary">Dalsorszám megjelenítése a dal címe előtt, ha létezik.</string>
<string name="settings.custom_sort">Egyéni rendezés</string>
<string name="settings.custom_sort_summary">A kiszolgáló alapértelmezett rendezésének felülbírálása, rendezés a lemez sorszáma és a kiadás éve alapján.</string>
- <string name="settings.open_to_library">Médiakönyvtár megnyitása</string>
- <string name="settings.open_to_library_summary">A Médiakönyvtár megnyitása a Főoldal helyett.</string>
+ <string name="settings.open_to_library">Médiatár megnyitása</string>
+ <string name="settings.open_to_library_summary">A Médiatár megnyitása a Főoldal helyett.</string>
<string name="settings.network_title">Hálózat</string>
<string name="settings.max_bitrate_wifi">Max. audió bitráta - Wi-Fi</string>
<string name="settings.max_bitrate_mobile">Max. audió bitráta - Mobilhálózat</string>
@@ -478,6 +484,7 @@ <string name="admin.role.stream">Zene streamelése</string>
<string name="admin.role.jukebox">Jukebox vezérlése</string>
<string name="admin.role.share">Megosztások kezelése</string>
+ <string name="admin.role.lastfm">Last.fm funkció használata</string>
<string name="music_service.retry">Hálózati hiba történt! Újrapróbálkozás %1$d/%2$d.</string>
@@ -494,8 +501,9 @@ <string name="parser.upgrade_server">Nem kompatibilis verzió. Kérjük, frissítse a Subsonic kiszolgálót!</string>
<string name="parser.not_authenticated">Hibás felhasználónév vagy jelszó!</string>
<string name="parser.not_authorized">Nincs engedélyezve! Ellenőrizze a felhasználó jogosultságait a Subsonic kiszolgálón!</string>
- <string name="parser.artist_count">%d előadó található a médiakönyvtárban.</string>
+ <string name="parser.artist_count">%d előadó található a médiatárban.</string>
<string name="parser.server_error">Kiszolgáló hiba: %s</string>
+ <string name="parser.scan_count">%d tétel átvizsgálva.</string>
<string name="select_artist.refresh">Frissítés</string>
<string name="select_artist.folder">Mappa kiválasztása</string>
diff --git a/res/values/strings.xml b/res/values/strings.xml index 71a74db6..b02cdc9d 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>
@@ -260,6 +264,8 @@ <string name="settings.server_username">Username</string>
<string name="settings.server_password">Password</string>
<string name="settings.server_open_browser">Open in browser</string>
+ <string name="settings.server_sync_summary">Whether or not syncing is enabled for this server</string>
+ <string name="settings.server_sync">Sync Enabled</string>
<string name="settings.cache_title">Music cache</string>
<string name="settings.preload_wifi">Songs to preload (Wifi)</string>
<string name="settings.preload_mobile">Songs to preload (Mobile)</string>
diff --git a/res/xml/changelog.xml b/res/xml/changelog.xml index e4f4a607..36c2859d 100644 --- a/res/xml/changelog.xml +++ b/res/xml/changelog.xml @@ -1,5 +1,13 @@ <?xml version="1.0" encoding="utf-8"?> <changelog> + <release version="4.7.5" versioncode="121" releasedate="8/24/2014"> + <change>Bookmarks: Resume from albums/playlists</change> + <change>Bookmarks: Auto bookmark long songs/podcasts</change> + <change>Bookmarks: Auto delete after played</change> + <change>Bookmarks Tab: Show artist/album</change> + <change>Bookmarks: Delete from album/playlist</change> + <change>Stars: Update without refresh</change> + </release> <release version="4.7.4" versioncode="120" releasedate="8/8/2014"> <change>Tasker Plugin: Start + optional start Shuffle Mode</change> <change>Madsonic 5.1+: Add Rescan Server option to Home tab</change> diff --git a/src/github/daneren2005/dsub/activity/SettingsActivity.java b/src/github/daneren2005/dsub/activity/SettingsActivity.java index 48f67dd5..e40a62d2 100644 --- a/src/github/daneren2005/dsub/activity/SettingsActivity.java +++ b/src/github/daneren2005/dsub/activity/SettingsActivity.java @@ -394,6 +394,12 @@ public class SettingsActivity extends PreferenceActivity implements SharedPrefer serverTagPreference.setTitle(R.string.settings_browse_by_tags); serverPasswordPreference.setDialogTitle(R.string.settings_server_password); + final CheckBoxPreference serverSyncPreference = new CheckBoxPreference(this); + serverSyncPreference.setKey(Constants.PREFERENCES_KEY_SERVER_SYNC + instance); + serverSyncPreference.setChecked(Util.isSyncEnabled(this, instance)); + serverSyncPreference.setSummary(R.string.settings_server_sync_summary); + serverSyncPreference.setTitle(R.string.settings_server_sync); + final Preference serverOpenBrowser = new Preference(this); serverOpenBrowser.setKey(Constants.PREFERENCES_KEY_OPEN_BROWSER); serverOpenBrowser.setPersistent(false); @@ -461,6 +467,7 @@ public class SettingsActivity extends PreferenceActivity implements SharedPrefer screen.addPreference(serverUsernamePreference); screen.addPreference(serverPasswordPreference); screen.addPreference(serverTagPreference); + screen.addPreference(serverSyncPreference); screen.addPreference(serverTestConnectionPreference); screen.addPreference(serverOpenBrowser); screen.addPreference(serverRemoveServerPreference); 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..3d199522 100644 --- a/src/github/daneren2005/dsub/domain/MusicDirectory.java +++ b/src/github/daneren2005/dsub/domain/MusicDirectory.java @@ -107,30 +107,33 @@ 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 type; private int closeness; public void loadMetadata(File file) { @@ -392,6 +395,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/domain/Playlist.java b/src/github/daneren2005/dsub/domain/Playlist.java index 663fa2b0..7cd820c0 100644 --- a/src/github/daneren2005/dsub/domain/Playlist.java +++ b/src/github/daneren2005/dsub/domain/Playlist.java @@ -105,8 +105,24 @@ public class Playlist implements Serializable { this.pub = pub; } - @Override - public String toString() { - return name; - } -}
\ No newline at end of file + @Override + public String toString() { + return name; + } + + @Override + public boolean equals(Object o) { + if(o == this) { + return true; + } else if(o == null) { + return false; + } else if(o instanceof String) { + return o.equals(this.id); + } else if(o.getClass() != getClass()) { + return false; + } + + Playlist playlist = (Playlist) o; + return playlist.id.equals(this.id); + } +} diff --git a/src/github/daneren2005/dsub/fragments/MainFragment.java b/src/github/daneren2005/dsub/fragments/MainFragment.java index da0711ef..91902b52 100644 --- a/src/github/daneren2005/dsub/fragments/MainFragment.java +++ b/src/github/daneren2005/dsub/fragments/MainFragment.java @@ -21,6 +21,7 @@ import android.widget.CheckBox; import android.widget.ListView;
import android.widget.TextView;
import github.daneren2005.dsub.R;
+import github.daneren2005.dsub.domain.MusicDirectory;
import github.daneren2005.dsub.domain.ServerInfo;
import github.daneren2005.dsub.service.DownloadService;
import github.daneren2005.dsub.util.Constants;
@@ -42,6 +43,7 @@ import java.util.List; public class MainFragment extends SubsonicFragment {
private static final String TAG = MainFragment.class.getSimpleName();
private LayoutInflater inflater;
+ private TextView countView;
private static final int MENU_GROUP_SERVER = 10;
private static final int MENU_ITEM_SERVER_BASE = 100;
@@ -152,6 +154,7 @@ public class MainFragment extends SubsonicFragment { final View albumsTitle = buttons.findViewById(R.id.main_albums);
final View albumsNewestButton = buttons.findViewById(R.id.main_albums_newest);
+ countView = (TextView) buttons.findViewById(R.id.main_albums_recent_count);
final View albumsRandomButton = buttons.findViewById(R.id.main_albums_random);
final View albumsHighestButton = buttons.findViewById(R.id.main_albums_highest);
final View albumsRecentButton = buttons.findViewById(R.id.main_albums_recent);
@@ -207,6 +210,10 @@ public class MainFragment extends SubsonicFragment { }
});
setTitle(R.string.common_appname);
+
+ if(!Util.isOffline(context)) {
+ getMostRecentCount();
+ }
}
private void setActiveServer(int instance) {
@@ -250,6 +257,16 @@ public class MainFragment extends SubsonicFragment { SubsonicFragment fragment = new SelectYearFragment();
replaceFragment(fragment);
} else {
+ // Clear out recently added count when viewing
+ if("newest".equals(type)) {
+ SharedPreferences.Editor editor = Util.getPreferences(context).edit();
+ editor.putInt(Constants.PREFERENCES_KEY_RECENT_COUNT, 0);
+ editor.commit();
+
+ // Clear immediately so doesn't still show when pressing back
+ setMostRecentCount(0);
+ }
+
SubsonicFragment fragment = new SelectDirectoryFragment();
Bundle args = new Bundle();
args.putString(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_TYPE, type);
@@ -442,4 +459,76 @@ public class MainFragment extends SubsonicFragment { }.execute();
} catch(Exception e) {}
}
+
+ private void getMostRecentCount() {
+ // Use stashed value until after refresh occurs
+ SharedPreferences prefs = Util.getPreferences(context);
+ final int startCount = prefs.getInt(Constants.PREFERENCES_KEY_RECENT_COUNT, 0);
+ setMostRecentCount(startCount);
+
+ new SilentBackgroundTask<Integer>(context) {
+ @Override
+ public Integer doInBackground() throws Exception {
+ String recentAddedFile = Util.getCacheName(context, "recent_count");
+ ArrayList<String> recents = FileUtil.deserialize(context, recentAddedFile, ArrayList.class);
+ if(recents == null) {
+ recents = new ArrayList<String>();
+ }
+
+ MusicService musicService = MusicServiceFactory.getMusicService(context);
+ MusicDirectory recentlyAdded = musicService.getAlbumList("newest", 20, 0, context, null);
+
+ // If first run, just put everything in it and return 0
+ boolean firstRun = recents.isEmpty();
+
+ // Count how many new albums are in the list
+ int count = 0;
+ for(MusicDirectory.Entry album: recentlyAdded.getChildren()) {
+ if(!recents.contains(album.getId())) {
+ recents.add(album.getId());
+ count++;
+ }
+ }
+ FileUtil.serialize(context, recents, recentAddedFile);
+
+ if(firstRun) {
+ return 0;
+ } else {
+ // Add the old count which will get cleared out after viewing recents
+ count += startCount;
+ SharedPreferences.Editor editor = Util.getPreferences(context).edit();
+ editor.putInt(Constants.PREFERENCES_KEY_RECENT_COUNT, count);
+ editor.commit();
+
+ return count;
+ }
+ }
+
+ @Override
+ public void done(Integer result) {
+ setMostRecentCount(result);
+ }
+
+ @Override
+ public void error(Throwable x) {
+ Log.w(TAG, "Failed to refresh most recent count", x);
+ }
+ }.execute();
+ }
+
+ private void setMostRecentCount(int count) {
+ if(count <= 0) {
+ countView.setVisibility(View.GONE);
+ } else {
+ String displayValue;
+ if(count < 10) {
+ displayValue = "0" + count;
+ } else {
+ displayValue = "" + count;
+ }
+
+ countView.setText(displayValue);
+ countView.setVisibility(View.VISIBLE);
+ }
+ }
}
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/SelectArtistFragment.java b/src/github/daneren2005/dsub/fragments/SelectArtistFragment.java index a06adca2..d0ea96b3 100644 --- a/src/github/daneren2005/dsub/fragments/SelectArtistFragment.java +++ b/src/github/daneren2005/dsub/fragments/SelectArtistFragment.java @@ -14,6 +14,7 @@ import android.view.ViewGroup; import android.widget.AbsListView;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
+import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.TextView;
import github.daneren2005.dsub.R;
@@ -205,6 +206,26 @@ public class SelectArtistFragment extends SelectListFragment<Artist> { }
}
+ @Override
+ public void setEmpty(boolean empty) {
+ super.setEmpty(empty);
+
+ if(empty && !Util.isOffline(context)) {
+ createMusicFolderButton();
+ setMusicFolders();
+
+ objects.clear();
+ listView.setAdapter(new ArtistAdapter(context, objects));
+ listView.setVisibility(View.VISIBLE);
+
+ View view = rootView.findViewById(R.id.tab_progress);
+ LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) view.getLayoutParams();
+ params.height = 0;
+ params.weight = 5;
+ view.setLayoutParams(params);
+ }
+ }
+
private void setMusicFolders() {
// Display selected music folder
if (musicFolders != 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 09a2e42b..a8b1f96b 100644 --- a/src/github/daneren2005/dsub/fragments/SelectDirectoryFragment.java +++ b/src/github/daneren2005/dsub/fragments/SelectDirectoryFragment.java @@ -342,27 +342,24 @@ public class SelectDirectoryFragment extends SubsonicFragment implements Adapter AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuItem.getMenuInfo();
Object selectedItem;
+ int headers = entryList.getHeaderViewsCount();
if(albumContext) {
- selectedItem = albums.get(showHeader ? (info.position - 1) : info.position);
+ selectedItem = albums.get(info.position);
} else {
if(info.position == 0) {
return false;
}
- info.position--;
- selectedItem = entries.get(showHeader ? (info.position - 1) : info.position);
+ selectedItem = entries.get(info.position - headers);
}
if(Util.getPreferences(context).getBoolean(Constants.PREFERENCES_KEY_PLAY_NOW_AFTER, false) && menuItem.getItemId() == R.id.song_menu_play_now) {
List<MusicDirectory.Entry> songs = new ArrayList<MusicDirectory.Entry>();
- Iterator it = entries.listIterator(info.position - 1);
+ Iterator it = entries.listIterator(info.position - headers);
while(it.hasNext()) {
songs.add((MusicDirectory.Entry) it.next());
}
- getDownloadService().clear();
- getDownloadService().download(songs, false, true, true, false);
- Util.startActivityWithoutTransition(context, DownloadActivity.class);
-
+ playNow(songs);
return true;
}
@@ -372,7 +369,7 @@ public class SelectDirectoryFragment extends SubsonicFragment implements Adapter switch (menuItem.getItemId()) {
case R.id.song_menu_remove_playlist:
- removeFromPlaylist(playlistId, playlistName, Arrays.<Integer>asList(info.position - 1));
+ removeFromPlaylist(playlistId, playlistName, Arrays.<Integer>asList(info.position - headers));
break;
case R.id.song_menu_server_download:
downloadPodcastEpisode((PodcastEpisode)selectedItem);
@@ -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 {
@@ -1064,6 +1065,11 @@ public class SelectDirectoryFragment extends SubsonicFragment implements Adapter }
}
musicService.setStarred(ids, artists, albums, parents, false, this, context);
+
+ for(MusicDirectory.Entry entry: unstar) {
+ setEntryStarred(entry, false);
+ }
+
return null;
}
diff --git a/src/github/daneren2005/dsub/fragments/SubsonicFragment.java b/src/github/daneren2005/dsub/fragments/SubsonicFragment.java index 7adb9115..9e5d936b 100644 --- a/src/github/daneren2005/dsub/fragments/SubsonicFragment.java +++ b/src/github/daneren2005/dsub/fragments/SubsonicFragment.java @@ -34,14 +34,18 @@ import android.support.v4.widget.SwipeRefreshLayout; import android.util.Log;
import android.view.ContextMenu;
import android.view.GestureDetector;
+import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
+import android.view.ViewGroup;
import android.widget.AbsListView;
+import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.EditText;
+import android.widget.LinearLayout;
import android.widget.TextView;
import github.daneren2005.dsub.R;
import github.daneren2005.dsub.activity.DownloadActivity;
@@ -69,6 +73,7 @@ import github.daneren2005.dsub.util.SilentBackgroundTask; import github.daneren2005.dsub.util.LoadingTask;
import github.daneren2005.dsub.util.UserUtil;
import github.daneren2005.dsub.util.Util;
+import github.daneren2005.dsub.view.PlaylistSongView;
import github.daneren2005.dsub.view.UpdateView;
import java.io.File;
@@ -83,6 +88,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 +169,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 +193,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 +227,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 +266,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 +329,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 +370,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 +657,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);
@@ -665,6 +686,8 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR musicService.setStarred(Arrays.asList(entry.getId()), null, null, parents, starred, null, context);
}
+ setEntryStarred(entry, starred);
+
return null;
}
@@ -690,6 +713,26 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR }
}.execute();
}
+ 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) {
+ Entry check = file.getSong();
+ if(entry.getId().equals(check.getId())) {
+ check.setStarred(starred);
+ downloadService.serializeQueue();
+ break;
+ }
+ }
+ }
+
+ Entry find = UpdateView.findEntry(entry);
+ if(find != null) {
+ find.setStarred(starred);
+ }
+ }
+
public void toggleStarred(final Artist entry) {
final boolean starred = !entry.isStarred();
entry.setStarred(starred);
@@ -744,6 +787,8 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR protected void downloadRecursively(final String id, final String name, final boolean isDirectory, final boolean save, final boolean append, final boolean autoplay, final boolean shuffle, final boolean background, final boolean playNext) {
LoadingTask<Boolean> task = new LoadingTask<Boolean>(context) {
private static final int MAX_SONGS = 500;
+ private boolean playNowOverride = false;
+ private List<Entry> songs;
@Override
protected Boolean doInBackground() throws Throwable {
@@ -767,12 +812,18 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR Collections.shuffle(root.getChildren());
}
- List<MusicDirectory.Entry> songs = new LinkedList<MusicDirectory.Entry>();
+ songs = new LinkedList<Entry>();
getSongsRecursively(root, songs);
DownloadService downloadService = getDownloadService();
boolean transition = false;
if (!songs.isEmpty() && downloadService != null) {
+ // Conditions for a standard play now operation
+ if(!append && !save && autoplay && !playNext && !shuffle && !background) {
+ playNowOverride = true;
+ return false;
+ }
+
if (!append) {
downloadService.clear();
}
@@ -791,17 +842,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;
@@ -816,6 +867,11 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR @Override
protected void done(Boolean result) {
+ if(playNowOverride) {
+ playNow(songs);
+ return;
+ }
+
warnIfNetworkOrStorageUnavailable();
if(result) {
@@ -839,7 +895,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;
@@ -866,25 +922,39 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR @Override
protected void done(final List<Playlist> playlists) {
- List<String> names = new ArrayList<String>();
- String createNew = context.getResources().getString(R.string.playlist_create_new);
- names.add(createNew);
- for(Playlist playlist: playlists) {
- names.add(playlist.getName());
- }
-
- AlertDialog.Builder builder = new AlertDialog.Builder(context);
- builder.setTitle(R.string.playlist_add_to)
- .setItems(names.toArray(new CharSequence[names.size()]), new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int which) {
+ // Create adapter to show playlists
+ Playlist createNew = new Playlist("-1", context.getResources().getString(R.string.playlist_create_new));
+ playlists.add(0, createNew);
+ ArrayAdapter playlistAdapter = new ArrayAdapter<Playlist>(context, R.layout.basic_count_item, playlists) {
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ Playlist playlist = getItem(position);
- if(which > 0) {
- addToPlaylist(playlists.get(which - 1), songs);
+ // Create new if not getting a convert view to use
+ PlaylistSongView view;
+ if(convertView instanceof PlaylistSongView) {
+ view = (PlaylistSongView) convertView;
} else {
- createNewPlaylist(songs, false);
+ view = new PlaylistSongView(context);
}
+
+ view.setObject(playlist, songs);
+
+ return view;
}
- });
+ };
+
+ AlertDialog.Builder builder = new AlertDialog.Builder(context);
+ builder.setTitle(R.string.playlist_add_to)
+ .setAdapter(playlistAdapter, new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int which) {
+ if (which > 0) {
+ addToPlaylist(playlists.get(which), songs);
+ } else {
+ createNewPlaylist(songs, false);
+ }
+ }
+ });
AlertDialog dialog = builder.create();
dialog.show();
}
@@ -903,7 +973,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 {
@@ -931,7 +1001,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);
@@ -989,7 +1059,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 {
@@ -1010,13 +1080,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;
}
@@ -1040,7 +1110,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;
@@ -1112,7 +1182,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 {
@@ -1120,7 +1190,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);
@@ -1128,7 +1198,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 {
@@ -1148,7 +1218,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);
@@ -1160,7 +1230,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);
@@ -1190,7 +1260,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();
}
@@ -1206,7 +1276,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;
@@ -1217,7 +1287,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)) {
@@ -1231,13 +1301,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);
- public void createShare(final List<MusicDirectory.Entry> entries) {
+ 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<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());
}
@@ -1278,4 +1379,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 027f6694..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"); @@ -415,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; } @@ -431,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; } @@ -557,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) { @@ -595,6 +631,27 @@ public class CachedMusicService implements MusicService { } }.execute(); } + + // Update playlist caches if there is at least one song to be starred + if(ids != null && ids.size() > 0) { + 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(Entry result) { + result.setStarred(starred); + } + }.execute(); + } } @Override @@ -740,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 @@ -872,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() { @@ -899,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); } } @@ -945,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 @@ -962,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 76e2dd89..a21bbd91 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; @@ -109,7 +111,7 @@ public class DownloadService extends Service { private final Handler handler = new Handler(); private Handler mediaPlayerHandler; private final DownloadServiceLifecycleSupport lifecycleSupport = new DownloadServiceLifecycleSupport(this); - private final ShufflePlayBuffer shufflePlayBuffer = new ShufflePlayBuffer(this); + private ShufflePlayBuffer shufflePlayBuffer; private final LruCache<MusicDirectory.Entry, DownloadFile> downloadFileCache = new LruCache<MusicDirectory.Entry, DownloadFile>(100); private final List<DownloadFile> cleanupCandidates = new ArrayList<DownloadFile>(); @@ -222,6 +224,7 @@ public class DownloadService extends Service { instance = this; lifecycleSupport.onCreate(); + shufflePlayBuffer = new ShufflePlayBuffer(this); } @Override @@ -294,17 +297,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,12 +339,14 @@ public class DownloadService extends Service { } if (autoplay) { - play(0); + play(start, true, position); } else { if (currentPlaying == null) { currentPlaying = downloadList.get(0); currentPlayingIndex = 0; currentPlaying.setPlaying(true); + } else { + currentPlayingIndex = downloadList.indexOf(currentPlaying); } checkDownloads(); } @@ -562,7 +560,7 @@ public class DownloadService extends Service { lifecycleSupport.post(new Runnable() { @Override public void run() { - if(online) { + if (online) { checkDownloads(); } else { clearIncomplete(); @@ -580,12 +578,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(); } } @@ -593,6 +588,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(); @@ -903,14 +906,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(); @@ -1360,7 +1360,7 @@ public class DownloadService extends Service { } catch(Throwable e) { mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC); } - String dataSource = file.getPath(); + String dataSource = file.getAbsolutePath(); if(isPartial) { if (proxy == null) { proxy = new BufferProxy(this); @@ -1379,7 +1379,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); } } @@ -1508,6 +1508,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) { @@ -1598,6 +1599,12 @@ public class DownloadService extends Service { } } + public synchronized void serializeQueue() { + if(playerState == PlayerState.PAUSED) { + lifecycleSupport.serializeDownloadQueue(); + } + } + private void handleError(Exception x) { Log.w(TAG, "Media player error: " + x, x); if(mediaPlayer != null) { @@ -1779,6 +1786,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..788057d2 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; @@ -67,6 +66,7 @@ import java.util.SortedSet; public class OfflineMusicService implements MusicService { private static final String TAG = OfflineMusicService.class.getSimpleName(); private static final String ERRORMSG = "Not available in offline mode"; + private static final Random random = new Random(); @Override public void ping(Context context, ProgressListener progressListener) throws Exception { @@ -669,7 +669,6 @@ public class OfflineMusicService implements MusicService { if (children.isEmpty()) { return result; } - Random random = new Random(); for (int i = 0; i < size; i++) { File file = children.get(random.nextInt(children.size())); result.addChild(createEntry(context, file, getName(file))); @@ -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 1559587a..d6715e8c 100644 --- a/src/github/daneren2005/dsub/service/RESTMusicService.java +++ b/src/github/daneren2005/dsub/service/RESTMusicService.java @@ -1138,7 +1138,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); @@ -1150,7 +1150,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)); @@ -1162,7 +1162,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/service/sync/PlaylistSyncAdapter.java b/src/github/daneren2005/dsub/service/sync/PlaylistSyncAdapter.java index bd6d056b..c0aecfa2 100644 --- a/src/github/daneren2005/dsub/service/sync/PlaylistSyncAdapter.java +++ b/src/github/daneren2005/dsub/service/sync/PlaylistSyncAdapter.java @@ -29,6 +29,7 @@ import java.util.List; import github.daneren2005.dsub.R;
import github.daneren2005.dsub.domain.MusicDirectory;
+import github.daneren2005.dsub.domain.Playlist;
import github.daneren2005.dsub.service.DownloadFile;
import github.daneren2005.dsub.service.parser.SubsonicRESTException;
import github.daneren2005.dsub.util.FileUtil;
@@ -43,6 +44,8 @@ import github.daneren2005.dsub.util.Util; public class PlaylistSyncAdapter extends SubsonicSyncAdapter {
private static String TAG = PlaylistSyncAdapter.class.getSimpleName();
+ // Update playlists at least once a week
+ private static int MAX_PLAYLIST_AGE = 24 * 7;
public PlaylistSyncAdapter(Context context, boolean autoInitialize) {
super(context, autoInitialize);
@@ -56,9 +59,10 @@ public class PlaylistSyncAdapter extends SubsonicSyncAdapter { public void onExecuteSync(Context context, int instance) {
String serverName = Util.getServerName(context, instance);
+ List<Playlist> remainder = null;
try {
// Just update playlist listings so user doesn't have to
- musicService.getPlaylists(true, context, null);
+ remainder = musicService.getPlaylists(true, context, null);
} catch(Exception e) {
Log.e(TAG, "Failed to refresh playlist list for " + serverName);
}
@@ -69,6 +73,12 @@ public class PlaylistSyncAdapter extends SubsonicSyncAdapter { for(int i = 0; i < playlistList.size(); i++) {
SyncSet cachedPlaylist = playlistList.get(i);
String id = cachedPlaylist.id;
+
+ // Remove playlist from remainder list
+ if(remainder != null) {
+ remainder.remove(new Playlist(id, ""));
+ }
+
try {
MusicDirectory playlist = musicService.getPlaylist(true, id, serverName, context, null);
@@ -121,6 +131,20 @@ public class PlaylistSyncAdapter extends SubsonicSyncAdapter { SyncUtil.setSyncedPlaylists(context, instance, playlistList);
}
}
+
+ // For remaining playlists, check to make sure they have been updated recently
+ if(remainder != null) {
+ for (Playlist playlist : remainder) {
+ MusicDirectory dir = FileUtil.deserialize(context, Util.getCacheName(context, instance, "playlist", playlist.getId()), MusicDirectory.class, MAX_PLAYLIST_AGE);
+ if (dir == null) {
+ try {
+ musicService.getPlaylist(true, playlist.getId(), serverName, context, null);
+ } catch(Exception e) {
+ Log.w(TAG, "Failed to update playlist for " + playlist.getName());
+ }
+ }
+ }
+ }
if(updated.size() > 0) {
Notifications.showSyncNotification(context, R.string.sync_new_playlists, SyncUtil.joinNames(updated));
diff --git a/src/github/daneren2005/dsub/service/sync/SubsonicSyncAdapter.java b/src/github/daneren2005/dsub/service/sync/SubsonicSyncAdapter.java index d9bf0e24..661f126d 100644 --- a/src/github/daneren2005/dsub/service/sync/SubsonicSyncAdapter.java +++ b/src/github/daneren2005/dsub/service/sync/SubsonicSyncAdapter.java @@ -108,10 +108,12 @@ public class SubsonicSyncAdapter extends AbstractThreadedSyncAdapter { int servers = Util.getServerCount(context); for(int i = 1; i <= servers; i++) { try { - if(isValidServer(context, i)) { + if(isValidServer(context, i) && Util.isSyncEnabled(context, i)) { tagBrowsing = Util.isTagBrowsing(context, i); musicService.setInstance(i); onExecuteSync(context, i); + } else { + Log.i(TAG, "Skipped sync for " + i); } } catch(Exception e) { Log.e(TAG, "Failed sync for " + className + "(" + i + ")", e); diff --git a/src/github/daneren2005/dsub/util/BackgroundTask.java b/src/github/daneren2005/dsub/util/BackgroundTask.java index 6627d19a..fda881c9 100644 --- a/src/github/daneren2005/dsub/util/BackgroundTask.java +++ b/src/github/daneren2005/dsub/util/BackgroundTask.java @@ -48,7 +48,7 @@ public abstract class BackgroundTask<T> implements ProgressListener { protected OnCancelListener cancelListener; protected Task task; - private static final int DEFAULT_CONCURRENCY = 5; + private static final int DEFAULT_CONCURRENCY = 8; private static final Collection<Thread> threads = Collections.synchronizedCollection(new ArrayList<Thread>()); protected static final BlockingQueue<BackgroundTask.Task> queue = new LinkedBlockingQueue<BackgroundTask.Task>(10); private static Handler handler = null; diff --git a/src/github/daneren2005/dsub/util/Constants.java b/src/github/daneren2005/dsub/util/Constants.java index a11d2448..5fb872f2 100644 --- a/src/github/daneren2005/dsub/util/Constants.java +++ b/src/github/daneren2005/dsub/util/Constants.java @@ -143,6 +143,8 @@ public final class Constants { public static final String PREFERENCES_KEY_ADMIN_ENABLED = "adminEnabled"; public static final String PREFERENCES_KEY_PLAYLIST_NAME = "suggestedPlaylistName"; public static final String PREFERENCES_KEY_PLAYLIST_ID = "suggestedPlaylistId"; + public static final String PREFERENCES_KEY_SERVER_SYNC = "serverSync"; + public static final String PREFERENCES_KEY_RECENT_COUNT = "mostRecentCount"; public static final String OFFLINE_SCROBBLE_COUNT = "scrobbleCount"; public static final String OFFLINE_SCROBBLE_ID = "scrobbleID"; diff --git a/src/github/daneren2005/dsub/util/FileUtil.java b/src/github/daneren2005/dsub/util/FileUtil.java index ac61351b..7371b46b 100644 --- a/src/github/daneren2005/dsub/util/FileUtil.java +++ b/src/github/daneren2005/dsub/util/FileUtil.java @@ -630,7 +630,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); @@ -670,7 +670,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/LoadingTask.java b/src/github/daneren2005/dsub/util/LoadingTask.java index 6c3385d2..77622e1e 100644 --- a/src/github/daneren2005/dsub/util/LoadingTask.java +++ b/src/github/daneren2005/dsub/util/LoadingTask.java @@ -38,13 +38,17 @@ public abstract class LoadingTask<T> extends BackgroundTask<T> { queue.offer(task = new Task() {
@Override
public void onDone(T result) {
- loading.dismiss();
+ if(loading.isShowing()) {
+ loading.dismiss();
+ }
done(result);
}
@Override
public void onError(Throwable t) {
- loading.dismiss();
+ if(loading.isShowing()) {
+ loading.dismiss();
+ }
error(t);
}
});
diff --git a/src/github/daneren2005/dsub/util/ShufflePlayBuffer.java b/src/github/daneren2005/dsub/util/ShufflePlayBuffer.java index 5a728334..5235a1f3 100644 --- a/src/github/daneren2005/dsub/util/ShufflePlayBuffer.java +++ b/src/github/daneren2005/dsub/util/ShufflePlayBuffer.java @@ -42,8 +42,6 @@ public class ShufflePlayBuffer { private static final String TAG = ShufflePlayBuffer.class.getSimpleName(); private static final String CACHE_FILENAME = "shuffleBuffer.ser"; - private static final int CAPACITY = 50; - private static final int REFILL_THRESHOLD = 40; private ScheduledExecutorService executorService; private Runnable runnable; @@ -52,6 +50,8 @@ public class ShufflePlayBuffer { private int lastCount = -1; private DownloadService context; private boolean awaitingResults = false; + private int capacity; + private int refillThreshold; private SharedPreferences.OnSharedPreferenceChangeListener listener; private int currentServer; @@ -71,6 +71,15 @@ public class ShufflePlayBuffer { } }; executorService.scheduleWithFixedDelay(runnable, 1, 10, TimeUnit.SECONDS); + + // Calculate out the capacity and refill threshold based on the user's random size preference + int shuffleListSize = Integer.parseInt(Util.getPreferences(context).getString(Constants.PREFERENCES_KEY_RANDOM_SIZE, "20")); + // ex: default 20 -> 50 + capacity = shuffleListSize * 5 / 2; + capacity = Math.min(500, capacity); + + // ex: default 20 -> 40 + refillThreshold = capacity * 4 / 5; } public List<MusicDirectory.Entry> get(int size) { @@ -105,7 +114,7 @@ public class ShufflePlayBuffer { private void restart() { synchronized(buffer) { - if(buffer.size() <= REFILL_THRESHOLD && lastCount != 0 && executorService.isShutdown()) { + if(buffer.size() <= refillThreshold && lastCount != 0 && executorService.isShutdown()) { executorService = Executors.newSingleThreadScheduledExecutor(); executorService.scheduleWithFixedDelay(runnable, 0, 10, TimeUnit.SECONDS); } @@ -116,14 +125,16 @@ public class ShufflePlayBuffer { // Check if active server has changed. clearBufferIfnecessary(); - if (buffer != null && (buffer.size() > REFILL_THRESHOLD || (!Util.isNetworkConnected(context) && !Util.isOffline(context)) || lastCount == 0)) { + if (buffer != null && (buffer.size() > refillThreshold || (!Util.isNetworkConnected(context) && !Util.isOffline(context)) || lastCount == 0)) { executorService.shutdown(); return; } try { MusicService service = MusicServiceFactory.getMusicService(context); - int n = CAPACITY - buffer.size(); + + // Get capacity based + int n = capacity - buffer.size(); String folder = null; if(!Util.isTagBrowsing(context)) { folder = Util.getSelectedMusicFolderId(context); @@ -131,9 +142,14 @@ public class ShufflePlayBuffer { MusicDirectory songs = service.getRandomSongs(n, folder, genre, startYear, endYear, context, null); synchronized (buffer) { - buffer.addAll(songs.getChildren()); - Log.i(TAG, "Refilled shuffle play buffer with " + songs.getChildrenSize() + " songs."); - lastCount = songs.getChildrenSize(); + lastCount = 0; + for(MusicDirectory.Entry entry: songs.getChildren()) { + if(!buffer.contains(entry)) { + buffer.add(entry); + lastCount++; + } + } + Log.i(TAG, "Refilled shuffle play buffer with " + lastCount + " songs."); // Cache buffer FileUtil.serialize(context, buffer, CACHE_FILENAME); diff --git a/src/github/daneren2005/dsub/util/Util.java b/src/github/daneren2005/dsub/util/Util.java index 1ea1a3be..c1d026d4 100644 --- a/src/github/daneren2005/dsub/util/Util.java +++ b/src/github/daneren2005/dsub/util/Util.java @@ -384,6 +384,25 @@ public final class Util { SharedPreferences prefs = getPreferences(context); return prefs.getBoolean(Constants.PREFERENCES_KEY_BROWSE_TAGS + instance, false); } + + public static boolean isSyncEnabled(Context context, int instance) { + SharedPreferences prefs = getPreferences(context); + return prefs.getBoolean(Constants.PREFERENCES_KEY_SERVER_SYNC + instance, true); + } + + 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); @@ -411,6 +430,21 @@ public final class Util { editor.putString(Constants.OFFLINE_SYNC_DEFAULT, defaultValue); editor.commit(); } + + public static String getCacheName(Context context, String name, String id) { + return getCacheName(context, getActiveServer(context), name, id); + } + public static String getCacheName(Context context, int instance, String name, String id) { + String s = getRestUrl(context, null, instance, false) + id; + return name + "-" + s.hashCode() + ".ser"; + } + public static String getCacheName(Context context, String name) { + return getCacheName(context, getActiveServer(context), name); + } + public static String getCacheName(Context context, int instance, String name) { + String s = getRestUrl(context, null, instance, false); + return name + "-" + s.hashCode() + ".ser"; + } public static int offlineScrobblesCount(Context context) { SharedPreferences offline = getOfflineSync(context); 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; diff --git a/src/github/daneren2005/dsub/view/PlaylistSongView.java b/src/github/daneren2005/dsub/view/PlaylistSongView.java new file mode 100644 index 00000000..53e9fafc --- /dev/null +++ b/src/github/daneren2005/dsub/view/PlaylistSongView.java @@ -0,0 +1,102 @@ +/*
+ This file is part of Subsonic.
+
+ Subsonic is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Subsonic is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Subsonic. If not, see <http://www.gnu.org/licenses/>.
+
+ Copyright 2009 (C) Sindre Mehus
+ */
+package github.daneren2005.dsub.view;
+
+import android.content.Context;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.ImageButton;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import java.util.List;
+
+import github.daneren2005.dsub.R;
+import github.daneren2005.dsub.domain.MusicDirectory;
+import github.daneren2005.dsub.domain.Playlist;
+import github.daneren2005.dsub.util.FileUtil;
+import github.daneren2005.dsub.util.SyncUtil;
+import github.daneren2005.dsub.util.Util;
+
+public class PlaylistSongView extends UpdateView {
+ private static final String TAG = PlaylistSongView.class.getSimpleName();
+
+ private Context context;
+ private Playlist playlist;
+
+ private TextView titleView;
+ private TextView countView;
+ private int count = 0;
+ private List<MusicDirectory.Entry> songs;
+
+ public PlaylistSongView(Context context) {
+ super(context, false);
+ this.context = context;
+ LayoutInflater.from(context).inflate(R.layout.basic_count_item, this, true);
+
+ titleView = (TextView) findViewById(R.id.basic_count_name);
+ countView = (TextView) findViewById(R.id.basic_count_count);
+ }
+
+ protected void setObjectImpl(Object obj1, Object obj2) {
+ this.playlist = (Playlist) obj1;
+ this.songs = (List<MusicDirectory.Entry>) obj2;
+ count = 0;
+ titleView.setText(playlist.getName());
+ // Make sure to hide initially so it's not present briefly before update
+ countView.setVisibility(View.GONE);
+ }
+
+ @Override
+ protected void updateBackground() {
+ // Make sure to reset when starting count
+ count = 0;
+
+ // Don't try to lookup playlist for Create New
+ if(!"-1".equals(playlist.getId())) {
+ MusicDirectory cache = FileUtil.deserialize(context, Util.getCacheName(context, "playlist", playlist.getId()), MusicDirectory.class);
+ if(cache != null) {
+ // Try to find song instances in the given playlists
+ for(MusicDirectory.Entry song: songs) {
+ if(cache.getChildren().contains(song)) {
+ count++;
+ }
+ }
+ }
+ }
+ }
+
+ @Override
+ protected void update() {
+ // Update count display with appropriate information
+ if(count <= 0) {
+ countView.setVisibility(View.GONE);
+ } else {
+ String displayName;
+ if(count < 10) {
+ displayName = "0" + count;
+ } else {
+ displayName = "" + count;
+ }
+
+ countView.setText(displayName);
+ countView.setVisibility(View.VISIBLE);
+ }
+ }
+}
diff --git a/src/github/daneren2005/dsub/view/SongView.java b/src/github/daneren2005/dsub/view/SongView.java index 55eff6f1..d5b7c0f2 100644 --- a/src/github/daneren2005/dsub/view/SongView.java +++ b/src/github/daneren2005/dsub/view/SongView.java @@ -248,4 +248,8 @@ public class SongView extends UpdateView implements Checkable { public void toggle() { checkedTextView.toggle(); } + + public MusicDirectory.Entry getEntry() { + return song; + } } diff --git a/src/github/daneren2005/dsub/view/UpdateView.java b/src/github/daneren2005/dsub/view/UpdateView.java index f30ec42a..5f34a765 100644 --- a/src/github/daneren2005/dsub/view/UpdateView.java +++ b/src/github/daneren2005/dsub/view/UpdateView.java @@ -33,6 +33,7 @@ import java.util.ArrayList; import java.util.List;
import java.util.WeakHashMap;
+import github.daneren2005.dsub.domain.MusicDirectory;
import github.daneren2005.dsub.util.ImageLoader;
import github.daneren2005.dsub.R;
import github.daneren2005.dsub.util.SilentBackgroundTask;
@@ -211,6 +212,19 @@ public class UpdateView extends LinearLayout { public static void removeActiveActivity() {
activeActivities--;
}
+
+ public static MusicDirectory.Entry findEntry(MusicDirectory.Entry entry) {
+ for(UpdateView view: INSTANCES.keySet()) {
+ if(view instanceof SongView) {
+ MusicDirectory.Entry check = ((SongView) view).getEntry();
+ if(check != null && entry != check && check.getId().equals(entry.getId())) {
+ return check;
+ }
+ }
+ }
+
+ return null;
+ }
protected void updateBackground() {
|