diff --git a/ultrasonic/src/main/AndroidManifest.xml b/ultrasonic/src/main/AndroidManifest.xml index 73ba4ece..fd808dae 100644 --- a/ultrasonic/src/main/AndroidManifest.xml +++ b/ultrasonic/src/main/AndroidManifest.xml @@ -57,6 +57,11 @@ a:configChanges="orientation|keyboardHidden" a:label="@string/playlist.label" a:launchMode="standard"/> + diff --git a/ultrasonic/src/main/java/org/moire/ultrasonic/Test/service/GetPodcastEpisodesTestReaderProvider.java b/ultrasonic/src/main/java/org/moire/ultrasonic/Test/service/GetPodcastEpisodesTestReaderProvider.java new file mode 100644 index 00000000..538ad595 --- /dev/null +++ b/ultrasonic/src/main/java/org/moire/ultrasonic/Test/service/GetPodcastEpisodesTestReaderProvider.java @@ -0,0 +1,85 @@ +package org.moire.ultrasonic.Test.service; + +import java.io.Reader; +import java.io.StringReader; + +/** + * Created by rcocula on 11/03/2016. + */ +public class GetPodcastEpisodesTestReaderProvider { + + private static String data = "\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + "\n"; + + + public static Reader getReader() { + + return new StringReader(data); + } +} diff --git a/ultrasonic/src/main/java/org/moire/ultrasonic/Test/service/GetPodcastTestReaderProvider.java b/ultrasonic/src/main/java/org/moire/ultrasonic/Test/service/GetPodcastTestReaderProvider.java new file mode 100644 index 00000000..91d3f62d --- /dev/null +++ b/ultrasonic/src/main/java/org/moire/ultrasonic/Test/service/GetPodcastTestReaderProvider.java @@ -0,0 +1,41 @@ +package org.moire.ultrasonic.Test.service; + +import java.io.Reader; +import java.io.StringReader; + +/** + * Created by rcocula on 11/03/2016. + */ +public class GetPodcastTestReaderProvider { + + private static String data = "\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + "\n"; + + + public static Reader getReader() { + + return new StringReader(data); + } +} diff --git a/ultrasonic/src/main/java/org/moire/ultrasonic/activity/PodcastsActivity.java b/ultrasonic/src/main/java/org/moire/ultrasonic/activity/PodcastsActivity.java new file mode 100644 index 00000000..74f9a223 --- /dev/null +++ b/ultrasonic/src/main/java/org/moire/ultrasonic/activity/PodcastsActivity.java @@ -0,0 +1,133 @@ +/* + 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 . + + Copyright 2009 (C) Sindre Mehus + */ + +package org.moire.ultrasonic.activity; + +import android.app.AlertDialog; +import android.app.ListActivity; +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.os.AsyncTask; +import android.os.Bundle; +import android.text.Editable; +import android.text.Spannable; +import android.text.SpannableString; +import android.text.method.LinkMovementMethod; +import android.text.util.Linkify; +import android.view.ContextMenu; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; +import android.view.View; +import android.widget.AdapterView; +import android.widget.CheckBox; +import android.widget.EditText; +import android.widget.ListView; +import android.widget.TextView; + +import com.handmark.pulltorefresh.library.PullToRefreshBase; +import com.handmark.pulltorefresh.library.PullToRefreshBase.OnRefreshListener; +import com.handmark.pulltorefresh.library.PullToRefreshListView; + +import org.moire.ultrasonic.R; +import org.moire.ultrasonic.domain.Playlist; +import org.moire.ultrasonic.domain.PodcastsChannel; +import org.moire.ultrasonic.service.MusicService; +import org.moire.ultrasonic.service.MusicServiceFactory; +import org.moire.ultrasonic.service.OfflineException; +import org.moire.ultrasonic.service.ServerTooOldException; +import org.moire.ultrasonic.util.BackgroundTask; +import org.moire.ultrasonic.util.CacheCleaner; +import org.moire.ultrasonic.util.Constants; +import org.moire.ultrasonic.util.LoadingTask; +import org.moire.ultrasonic.util.TabActivityBackgroundTask; +import org.moire.ultrasonic.util.Util; +import org.moire.ultrasonic.view.PlaylistAdapter; +import org.moire.ultrasonic.view.PodcastsChannelsAdapter; + +import java.util.List; + +public class PodcastsActivity extends SubsonicTabActivity { + + private View emptyTextView; + SubsonicTabActivity currentActivity = null; + ListView channelItemsListView = null; + + Context currentContext = (Context)this; + + @Override + public void onCreate(Bundle savedInstanceState) + { + this.currentActivity = this; + + super.onCreate(savedInstanceState); + setContentView(R.layout.podcasts); + + + emptyTextView = findViewById(R.id.select_podcasts_empty); + channelItemsListView = (ListView)findViewById(R.id.podcasts_channels_items_list); + channelItemsListView.setOnItemClickListener(new AdapterView.OnItemClickListener() { + @Override + public void onItemClick(AdapterView parent, View view, int position, long id) { + PodcastsChannel pc = (PodcastsChannel) parent.getItemAtPosition(position); + if (pc == null) { + return; + } + + Intent intent = new Intent(currentContext, SelectAlbumActivity.class); + intent.putExtra(Constants.INTENT_EXTRA_NAME_PODCAST_CHANNEL_ID, pc.getId()); + startActivityForResultWithoutTransition(PodcastsActivity.this, intent); + } + }); + + load(); + } + + + + private void load() + { + BackgroundTask> task = new TabActivityBackgroundTask>(this, true) + { + @Override + protected List doInBackground() throws Throwable + { + MusicService musicService = MusicServiceFactory.getMusicService(PodcastsActivity.this); + List channels = musicService.getPodcastsChannels(false,PodcastsActivity.this, this); + + /* TODO c'est quoi ce nettoyage de cache ? + if (!Util.isOffline(PodcastsActivity.this)) + new CacheCleaner(PodcastsActivity.this, getDownloadService()).cleanPlaylists(playlists); + */ + return channels; + } + + @Override + protected void done(List result) + { + channelItemsListView.setAdapter(new PodcastsChannelsAdapter(currentActivity, result)); + emptyTextView.setVisibility(result.isEmpty() ? View.VISIBLE : View.GONE); + } + }; + task.execute(); + } + + +} \ No newline at end of file diff --git a/ultrasonic/src/main/java/org/moire/ultrasonic/activity/SelectAlbumActivity.java b/ultrasonic/src/main/java/org/moire/ultrasonic/activity/SelectAlbumActivity.java index 3a3e9d6a..0958aabe 100644 --- a/ultrasonic/src/main/java/org/moire/ultrasonic/activity/SelectAlbumActivity.java +++ b/ultrasonic/src/main/java/org/moire/ultrasonic/activity/SelectAlbumActivity.java @@ -278,6 +278,7 @@ public class SelectAlbumActivity extends SubsonicTabActivity String name = getIntent().getStringExtra(Constants.INTENT_EXTRA_NAME_NAME); String parentId = getIntent().getStringExtra(Constants.INTENT_EXTRA_NAME_PARENT_ID); String playlistId = getIntent().getStringExtra(Constants.INTENT_EXTRA_NAME_PLAYLIST_ID); + String podcastChannelId = getIntent().getStringExtra(Constants.INTENT_EXTRA_NAME_PODCAST_CHANNEL_ID); String playlistName = getIntent().getStringExtra(Constants.INTENT_EXTRA_NAME_PLAYLIST_NAME); String shareId = getIntent().getStringExtra(Constants.INTENT_EXTRA_NAME_SHARE_ID); String shareName = getIntent().getStringExtra(Constants.INTENT_EXTRA_NAME_SHARE_NAME); @@ -297,6 +298,9 @@ public class SelectAlbumActivity extends SubsonicTabActivity { getPlaylist(playlistId, playlistName); } + else if (podcastChannelId != null) { + getPodcastEpisodes(podcastChannelId); + } else if (shareId != null) { getShare(shareId, shareName); @@ -852,6 +856,21 @@ public class SelectAlbumActivity extends SubsonicTabActivity }.execute(); } + private void getPodcastEpisodes(final String podcastChannelId) + { + // TODO on fait quoi là ? + //setActionBarSubtitle(playlistName); + + new LoadTask() + { + @Override + protected MusicDirectory load(MusicService service) throws Exception + { + return service.getPodcastEpisodes(podcastChannelId, SelectAlbumActivity.this, this); + } + }.execute(); + } + private void getShare(final String shareId, final CharSequence shareName) { setActionBarSubtitle(shareName); diff --git a/ultrasonic/src/main/java/org/moire/ultrasonic/activity/SubsonicTabActivity.java b/ultrasonic/src/main/java/org/moire/ultrasonic/activity/SubsonicTabActivity.java index d5a4ec08..3756c76a 100644 --- a/ultrasonic/src/main/java/org/moire/ultrasonic/activity/SubsonicTabActivity.java +++ b/ultrasonic/src/main/java/org/moire/ultrasonic/activity/SubsonicTabActivity.java @@ -151,6 +151,7 @@ public class SubsonicTabActivity extends ResultActivity implements OnClickListen findViewById(R.id.menu_browse).setOnClickListener(this); findViewById(R.id.menu_search).setOnClickListener(this); findViewById(R.id.menu_playlists).setOnClickListener(this); + findViewById(R.id.menu_podcasts).setOnClickListener(this); sharesMenuItem.setOnClickListener(this); chatMenuItem.setOnClickListener(this); bookmarksMenuItem.setOnClickListener(this); @@ -1436,6 +1437,11 @@ public class SubsonicTabActivity extends ResultActivity implements OnClickListen intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); startActivityForResultWithoutTransition(SubsonicTabActivity.this, intent); break; + case R.id.menu_podcasts: + intent = new Intent(SubsonicTabActivity.this, PodcastsActivity.class); + intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); + startActivityForResultWithoutTransition(SubsonicTabActivity.this, intent); + break; case R.id.menu_shares: intent = new Intent(SubsonicTabActivity.this, ShareActivity.class); intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); diff --git a/ultrasonic/src/main/java/org/moire/ultrasonic/domain/PodcastEpisode.java b/ultrasonic/src/main/java/org/moire/ultrasonic/domain/PodcastEpisode.java new file mode 100644 index 00000000..857cb420 --- /dev/null +++ b/ultrasonic/src/main/java/org/moire/ultrasonic/domain/PodcastEpisode.java @@ -0,0 +1,94 @@ +/* + 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 . + + Copyright 2009 (C) Sindre Mehus + */ +package org.moire.ultrasonic.domain; + +import java.io.Serializable; + +/** + * @author Sindre Mehus + */ +public class PodcastEpisode implements Serializable +{ + + /** + * + */ + private static final long serialVersionUID = -4160515427075433798L; + private String id; + private String title; + private String url; + private String description; + private String status; + + public PodcastEpisode(String id, String title, String url, String description, String status) + { + this.id = id; + this.title = title; + this.url = url; + this.description = description; + this.status = status; + } + + public String getId() + { + return id; + } + + public void setId(String id) + { + this.id = id; + } + + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public String getStatus() { + return status; + } + + public void setStatus(String status) { + this.status = status; + } + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + @Override + public String toString() { + return getTitle(); + } +} \ No newline at end of file diff --git a/ultrasonic/src/main/java/org/moire/ultrasonic/domain/PodcastsChannel.java b/ultrasonic/src/main/java/org/moire/ultrasonic/domain/PodcastsChannel.java new file mode 100644 index 00000000..b525c7d6 --- /dev/null +++ b/ultrasonic/src/main/java/org/moire/ultrasonic/domain/PodcastsChannel.java @@ -0,0 +1,94 @@ +/* + 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 . + + Copyright 2009 (C) Sindre Mehus + */ +package org.moire.ultrasonic.domain; + +import java.io.Serializable; + +/** + * @author Sindre Mehus + */ +public class PodcastsChannel implements Serializable +{ + + /** + * + */ + private static final long serialVersionUID = -4160515427075433798L; + private String id; + private String title; + private String url; + private String description; + private String status; + + public PodcastsChannel(String id, String title,String url, String description, String status) + { + this.id = id; + this.title = title; + this.url = url; + this.description = description; + this.status = status; + } + + public String getId() + { + return id; + } + + public void setId(String id) + { + this.id = id; + } + + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public String getStatus() { + return status; + } + + public void setStatus(String status) { + this.status = status; + } + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + @Override + public String toString() { + return getTitle(); + } +} \ No newline at end of file diff --git a/ultrasonic/src/main/java/org/moire/ultrasonic/service/CachedMusicService.java b/ultrasonic/src/main/java/org/moire/ultrasonic/service/CachedMusicService.java index e112084d..06938bb1 100644 --- a/ultrasonic/src/main/java/org/moire/ultrasonic/service/CachedMusicService.java +++ b/ultrasonic/src/main/java/org/moire/ultrasonic/service/CachedMusicService.java @@ -30,6 +30,7 @@ import org.moire.ultrasonic.domain.Lyrics; import org.moire.ultrasonic.domain.MusicDirectory; import org.moire.ultrasonic.domain.MusicFolder; import org.moire.ultrasonic.domain.Playlist; +import org.moire.ultrasonic.domain.PodcastsChannel; import org.moire.ultrasonic.domain.SearchCriteria; import org.moire.ultrasonic.domain.SearchResult; import org.moire.ultrasonic.domain.Share; @@ -66,6 +67,7 @@ public class CachedMusicService implements MusicService private final TimeLimitedCache cachedIndexes = new TimeLimitedCache(60 * 60, TimeUnit.SECONDS); private final TimeLimitedCache cachedArtists = new TimeLimitedCache(60 * 60, TimeUnit.SECONDS); private final TimeLimitedCache> cachedPlaylists = new TimeLimitedCache>(3600, TimeUnit.SECONDS); + private final TimeLimitedCache> cachedPodcastsChannels = new TimeLimitedCache>(3600, TimeUnit.SECONDS); private final TimeLimitedCache> cachedMusicFolders = new TimeLimitedCache>(10 * 3600, TimeUnit.SECONDS); private final TimeLimitedCache> cachedGenres = new TimeLimitedCache>(10 * 3600, TimeUnit.SECONDS); @@ -215,6 +217,24 @@ public class CachedMusicService implements MusicService return musicService.getPlaylist(id, name, context, progressListener); } + @Override + public List getPodcastsChannels(boolean refresh, Context context, ProgressListener progressListener) throws Exception { + checkSettingsChanged(context); + List result = refresh ? null : cachedPodcastsChannels.get(); + if (result == null) + { + result = musicService.getPodcastsChannels(refresh, context, progressListener); + cachedPodcastsChannels.set(result); + } + return result; + } + + @Override + public MusicDirectory getPodcastEpisodes(String podcastChannelId, Context context, ProgressListener progressListener) throws Exception { + return musicService.getPodcastEpisodes(podcastChannelId,context,progressListener); + } + + @Override public List getPlaylists(boolean refresh, Context context, ProgressListener progressListener) throws Exception { @@ -532,4 +552,5 @@ public class CachedMusicService implements MusicService { return musicService.getAvatar(context, username, size, saveToFile, highQuality, progressListener); } + } diff --git a/ultrasonic/src/main/java/org/moire/ultrasonic/service/MusicService.java b/ultrasonic/src/main/java/org/moire/ultrasonic/service/MusicService.java index 1889eb2a..7439efbb 100644 --- a/ultrasonic/src/main/java/org/moire/ultrasonic/service/MusicService.java +++ b/ultrasonic/src/main/java/org/moire/ultrasonic/service/MusicService.java @@ -21,6 +21,7 @@ package org.moire.ultrasonic.service; import android.content.Context; import android.graphics.Bitmap; +import org.moire.ultrasonic.activity.SelectAlbumActivity; import org.moire.ultrasonic.domain.Bookmark; import org.moire.ultrasonic.domain.ChatMessage; import org.moire.ultrasonic.domain.Genre; @@ -30,6 +31,8 @@ import org.moire.ultrasonic.domain.Lyrics; import org.moire.ultrasonic.domain.MusicDirectory; import org.moire.ultrasonic.domain.MusicFolder; import org.moire.ultrasonic.domain.Playlist; +import org.moire.ultrasonic.domain.PodcastEpisode; +import org.moire.ultrasonic.domain.PodcastsChannel; import org.moire.ultrasonic.domain.SearchCriteria; import org.moire.ultrasonic.domain.SearchResult; import org.moire.ultrasonic.domain.Share; @@ -74,6 +77,8 @@ public interface MusicService MusicDirectory getPlaylist(String id, String name, Context context, ProgressListener progressListener) throws Exception; + List getPodcastsChannels(boolean refresh, Context context, ProgressListener progressListener) throws Exception; + List getPlaylists(boolean refresh, Context context, ProgressListener progressListener) throws Exception; void createPlaylist(String id, String name, List entries, Context context, ProgressListener progressListener) throws Exception; @@ -149,4 +154,6 @@ public interface MusicService void updateShare(String id, String description, Long expires, Context context, ProgressListener progressListener) throws Exception; Bitmap getAvatar(Context context, String username, int size, boolean saveToFile, boolean highQuality, ProgressListener progressListener) throws Exception; + + MusicDirectory getPodcastEpisodes(String podcastChannelId, Context context, ProgressListener progressListener) throws Exception; } \ No newline at end of file diff --git a/ultrasonic/src/main/java/org/moire/ultrasonic/service/RESTMusicService.java b/ultrasonic/src/main/java/org/moire/ultrasonic/service/RESTMusicService.java index 8e2f0869..536102b1 100644 --- a/ultrasonic/src/main/java/org/moire/ultrasonic/service/RESTMusicService.java +++ b/ultrasonic/src/main/java/org/moire/ultrasonic/service/RESTMusicService.java @@ -26,6 +26,7 @@ import android.net.NetworkInfo; import android.util.Log; import org.moire.ultrasonic.R; +import org.moire.ultrasonic.Test.service.GetPodcastEpisodesTestReaderProvider; import org.moire.ultrasonic.domain.Bookmark; import org.moire.ultrasonic.domain.ChatMessage; import org.moire.ultrasonic.domain.Genre; @@ -35,6 +36,8 @@ import org.moire.ultrasonic.domain.Lyrics; import org.moire.ultrasonic.domain.MusicDirectory; import org.moire.ultrasonic.domain.MusicFolder; import org.moire.ultrasonic.domain.Playlist; +import org.moire.ultrasonic.domain.PodcastEpisode; +import org.moire.ultrasonic.domain.PodcastsChannel; import org.moire.ultrasonic.domain.SearchCriteria; import org.moire.ultrasonic.domain.SearchResult; import org.moire.ultrasonic.domain.ServerInfo; @@ -54,6 +57,8 @@ import org.moire.ultrasonic.service.parser.MusicDirectoryParser; import org.moire.ultrasonic.service.parser.MusicFoldersParser; import org.moire.ultrasonic.service.parser.PlaylistParser; import org.moire.ultrasonic.service.parser.PlaylistsParser; +import org.moire.ultrasonic.service.parser.PodcastEpisodeParser; +import org.moire.ultrasonic.service.parser.PodcastsChannelsParser; import org.moire.ultrasonic.service.parser.RandomSongsParser; import org.moire.ultrasonic.service.parser.SearchResult2Parser; import org.moire.ultrasonic.service.parser.SearchResultParser; @@ -66,6 +71,7 @@ import org.moire.ultrasonic.util.CancellableTask; import org.moire.ultrasonic.util.Constants; import org.moire.ultrasonic.util.FileUtil; import org.moire.ultrasonic.util.ProgressListener; +import org.moire.ultrasonic.util.StreamProxy; import org.moire.ultrasonic.util.Util; import org.apache.http.Header; @@ -104,6 +110,8 @@ import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.Reader; +import java.io.StringReader; +import java.lang.reflect.Array; import java.net.URLEncoder; import java.util.ArrayList; import java.util.Arrays; @@ -111,6 +119,7 @@ import java.util.Collection; import java.util.Collections; import java.util.LinkedList; import java.util.List; +import java.util.Objects; import java.util.concurrent.atomic.AtomicReference; import static java.util.Arrays.asList; @@ -594,6 +603,43 @@ public class RESTMusicService implements MusicService } } + @Override + public List getPodcastsChannels(boolean refresh, Context context, ProgressListener progressListener) throws Exception + { + Reader reader = getReader(context, progressListener, "getPodcasts", null,"includeEpisodes", "false"); + try { + return new PodcastsChannelsParser(context).parse(reader, progressListener); + } + finally + { + Util.close(reader); + } + } + + @Override + public MusicDirectory getPodcastEpisodes(String podcastChannelId, Context context, ProgressListener progressListener) throws Exception { + + List names = new ArrayList(); + names.add("id"); + names.add("includeEpisodes"); + List values = new ArrayList(); + values.add(podcastChannelId); + values.add("true"); + + // TODO + Reader reader = getReader(context, progressListener, "getPodcasts", null, names,values); + //Reader reader = GetPodcastEpisodesTestReaderProvider.getReader(); + try { + return new PodcastEpisodeParser(context).parse(reader, progressListener); + } + finally + { + Util.close(reader); + } + } + + + @Override public List getPlaylists(boolean refresh, Context context, ProgressListener progressListener) throws Exception { @@ -1741,4 +1787,5 @@ public class RESTMusicService implements MusicService return Util.scaleBitmap(bitmap, size); } } + } diff --git a/ultrasonic/src/main/java/org/moire/ultrasonic/service/parser/PodcastEpisodeParser.java b/ultrasonic/src/main/java/org/moire/ultrasonic/service/parser/PodcastEpisodeParser.java new file mode 100644 index 00000000..5b4c56c3 --- /dev/null +++ b/ultrasonic/src/main/java/org/moire/ultrasonic/service/parser/PodcastEpisodeParser.java @@ -0,0 +1,143 @@ +/* + 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 . + + Copyright 2009 (C) Sindre Mehus + */ +package org.moire.ultrasonic.service.parser; + +import android.content.Context; + +import org.moire.ultrasonic.R; +import org.moire.ultrasonic.domain.MusicDirectory; +import org.moire.ultrasonic.domain.PodcastEpisode; +import org.moire.ultrasonic.domain.PodcastsChannel; +import org.moire.ultrasonic.util.ProgressListener; +import org.xmlpull.v1.XmlPullParser; + +import java.io.Reader; +import java.text.DateFormat; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Comparator; +import java.util.Date; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Set; +import java.util.SortedMap; +import java.util.TreeMap; + +/** + * @author Sindre Mehus + */ +public class PodcastEpisodeParser extends AbstractParser +{ + + public PodcastEpisodeParser(Context context) + { + super(context); + } + + public MusicDirectory parse(Reader reader, ProgressListener progressListener) throws Exception + { + + MusicDirectory musicDirectory = new MusicDirectory(); + SortedMap sortedEntries = new TreeMap(); + + Locale currentLocale = getContext().getResources().getConfiguration().locale; + + DateFormat shortDateFormat = DateFormat.getDateTimeInstance( + DateFormat.SHORT, + DateFormat.SHORT, currentLocale); + DateFormat parseDateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS"); + + updateProgress(progressListener, R.string.parser_reading); + init(reader); + + int eventType; + do + { + eventType = nextParseEvent(); + if (eventType == XmlPullParser.START_TAG) + { + String tag = getElementName(); + if ("episode".equals(tag)) + { + String status = get("status"); + if (!"skipped".equals(status) && !"error".equals(status)) { + MusicDirectory.Entry entry = new MusicDirectory.Entry(); + String streamId = get("streamId"); + entry.setId(streamId); + entry.setIsDirectory(Boolean.parseBoolean(get("isDir"))); + entry.setIsVideo(Boolean.parseBoolean(get("isVideo"))); + entry.setType(get("type")); + entry.setPath(get("path")); + entry.setSuffix(get("suffix")); + String size = get("size"); + if (size != null) { + entry.setSize(Long.parseLong(size)); + } + entry.setCoverArt(get("coverArt")); + entry.setAlbum(get("album")); + entry.setTitle(get("title")); + entry.setAlbumId(get("albumId")); + entry.setArtist(get("artist")); + entry.setArtistId(get("artistId")); + String bitRate = get("bitRate"); + if (bitRate != null) { + entry.setBitRate(Integer.parseInt(get("bitRate"))); + } + entry.setContentType(get("contentType")); + String duration = get("duration"); + if (duration != null) { + entry.setDuration(Long.parseLong(duration)); + } + entry.setGenre(get("genre")); + entry.setParent(get("parent")); + entry.setCreated("created"); + + + String publishDate = get("publishDate"); + if (publishDate != null) { + try { + Date publishDateDate = parseDateFormat.parse(publishDate); + entry.setArtist(shortDateFormat.format(publishDateDate)); + sortedEntries.put(publishDateDate, entry); + } catch (Exception e) { + // nothing to do + } + } + } + } + else if ("error".equals(tag)) + { + handleError(); + } + } + } while (eventType != XmlPullParser.END_DOCUMENT); + + validate(); + updateProgress(progressListener, R.string.parser_reading_done); + + for (Date pubDate : sortedEntries.keySet()) { + musicDirectory.addFirst(sortedEntries.get(pubDate)); + } + return musicDirectory; + } +} + diff --git a/ultrasonic/src/main/java/org/moire/ultrasonic/service/parser/PodcastsChannelsParser.java b/ultrasonic/src/main/java/org/moire/ultrasonic/service/parser/PodcastsChannelsParser.java new file mode 100644 index 00000000..8c10a0cb --- /dev/null +++ b/ultrasonic/src/main/java/org/moire/ultrasonic/service/parser/PodcastsChannelsParser.java @@ -0,0 +1,81 @@ +/* + 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 . + + Copyright 2009 (C) Sindre Mehus + */ +package org.moire.ultrasonic.service.parser; + +import android.content.Context; + +import org.moire.ultrasonic.R; +import org.moire.ultrasonic.domain.Playlist; +import org.moire.ultrasonic.domain.PodcastsChannel; +import org.moire.ultrasonic.util.ProgressListener; +import org.moire.ultrasonic.view.PlaylistAdapter; +import org.xmlpull.v1.XmlPullParser; + +import java.io.Reader; +import java.util.ArrayList; +import java.util.List; + +/** + * @author Sindre Mehus + */ +public class PodcastsChannelsParser extends AbstractParser +{ + + public PodcastsChannelsParser(Context context) + { + super(context); + } + + public List parse(Reader reader, ProgressListener progressListener) throws Exception + { + + updateProgress(progressListener, R.string.parser_reading); + init(reader); + + List result = new ArrayList(); + int eventType; + do + { + eventType = nextParseEvent(); + if (eventType == XmlPullParser.START_TAG) + { + String tag = getElementName(); + if ("channel".equals(tag)) + { + String id = get("id"); + String title = get("title"); + String url = get("url"); + String description = get("description"); + String status = get("status"); + result.add(new PodcastsChannel(id,title, url,description,status)); + } + else if ("error".equals(tag)) + { + handleError(); + } + } + } while (eventType != XmlPullParser.END_DOCUMENT); + + validate(); + updateProgress(progressListener, R.string.parser_reading_done); + + return result; + } + +} \ No newline at end of file diff --git a/ultrasonic/src/main/java/org/moire/ultrasonic/util/Constants.java b/ultrasonic/src/main/java/org/moire/ultrasonic/util/Constants.java index 32a65b3a..19c4a199 100644 --- a/ultrasonic/src/main/java/org/moire/ultrasonic/util/Constants.java +++ b/ultrasonic/src/main/java/org/moire/ultrasonic/util/Constants.java @@ -41,6 +41,7 @@ public final class Constants public static final String INTENT_EXTRA_NAME_AUTOPLAY = "subsonic.playall"; public static final String INTENT_EXTRA_NAME_QUERY = "subsonic.query"; public static final String INTENT_EXTRA_NAME_PLAYLIST_ID = "subsonic.playlist.id"; + public static final String INTENT_EXTRA_NAME_PODCAST_CHANNEL_ID = "subsonic.podcastChannel.id"; public static final String INTENT_EXTRA_NAME_PARENT_ID = "subsonic.parent.id"; public static final String INTENT_EXTRA_NAME_PLAYLIST_NAME = "subsonic.playlist.name"; public static final String INTENT_EXTRA_NAME_SHARE_ID = "subsonic.share.id"; diff --git a/ultrasonic/src/main/java/org/moire/ultrasonic/view/PodcastsChannelsAdapter.java b/ultrasonic/src/main/java/org/moire/ultrasonic/view/PodcastsChannelsAdapter.java new file mode 100644 index 00000000..3e480a55 --- /dev/null +++ b/ultrasonic/src/main/java/org/moire/ultrasonic/view/PodcastsChannelsAdapter.java @@ -0,0 +1,83 @@ +package org.moire.ultrasonic.view; + +import android.app.Activity; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ArrayAdapter; +import android.widget.TextView; + +import org.moire.ultrasonic.R; +import org.moire.ultrasonic.activity.SubsonicTabActivity; +import org.moire.ultrasonic.domain.Playlist; +import org.moire.ultrasonic.domain.PodcastsChannel; + +import java.io.Serializable; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; + +/** + * @author Sindre Mehus + */ +public class PodcastsChannelsAdapter extends ArrayAdapter +{ + + //private final SubsonicTabActivity activity; + + public PodcastsChannelsAdapter(Activity activity, List channels) + { + super(activity, R.layout.podcasts_channel_item, channels); + //this.activity = activity; + } + + @Override + public void add(PodcastsChannel object) { + super.add(object); + } +/* @Override + public View getView(int position, View convertView, ViewGroup parent) + { + PodcastsChannel entry = getItem(position); + PlaylistView view; + + if (convertView != null && convertView instanceof PlaylistView) + { + PlaylistView currentView = (PlaylistView) convertView; + + ViewHolder viewHolder = (ViewHolder) convertView.getTag(); + view = currentView; + view.setViewHolder(viewHolder); + } + else + { + view = new PlaylistView(activity); + view.setLayout(); + } + + view.setPlaylist(entry); + return view; + } + */ + + /* public static class PlaylistComparator implements Comparator, Serializable + { + private static final long serialVersionUID = -6201663557439120008L; + + @Override + public int compare(Playlist playlist1, Playlist playlist2) + { + return playlist1.getName().compareToIgnoreCase(playlist2.getName()); + } + + public static List sort(List playlists) + { + Collections.sort(playlists, new PlaylistComparator()); + return playlists; + } + } */ + + /* static class ViewHolder + { + TextView name; + } */ +} \ No newline at end of file diff --git a/ultrasonic/src/main/java/org/moire/ultrasonic/view/PodcatsChannelItemView.java b/ultrasonic/src/main/java/org/moire/ultrasonic/view/PodcatsChannelItemView.java new file mode 100644 index 00000000..89163d86 --- /dev/null +++ b/ultrasonic/src/main/java/org/moire/ultrasonic/view/PodcatsChannelItemView.java @@ -0,0 +1,63 @@ +/* + 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 . + + Copyright 2009 (C) Sindre Mehus + */ +package org.moire.ultrasonic.view; + +import android.content.Context; +import android.view.LayoutInflater; +import android.widget.TextView; + +import org.moire.ultrasonic.R; +import org.moire.ultrasonic.domain.Playlist; + +/** + * Used to display playlists in a {@code ListView}. + * + * @author Sindre Mehus + */ +public class PodcatsChannelItemView extends UpdateView +{ + private Context context; + private PlaylistAdapter.ViewHolder viewHolder; + + public PodcatsChannelItemView(Context context) + { + super(context); + this.context = context; + } + + public void setLayout() + { + LayoutInflater.from(context).inflate(R.layout.playlist_list_item, this, true); + viewHolder = new PlaylistAdapter.ViewHolder(); + viewHolder.name = (TextView) findViewById(R.id.playlist_name); + setTag(viewHolder); + } + + public void setViewHolder(PlaylistAdapter.ViewHolder viewHolder) + { + this.viewHolder = viewHolder; + setTag(this.viewHolder); + } + + public void setPlaylist(Playlist playlist) + { + viewHolder.name.setText(playlist.getName()); + update(); + } +} \ No newline at end of file diff --git a/ultrasonic/src/main/res/drawable-hdpi/ic_menu_podcasts_dark.png b/ultrasonic/src/main/res/drawable-hdpi/ic_menu_podcasts_dark.png new file mode 100644 index 00000000..78734e48 Binary files /dev/null and b/ultrasonic/src/main/res/drawable-hdpi/ic_menu_podcasts_dark.png differ diff --git a/ultrasonic/src/main/res/drawable-hdpi/ic_menu_podcasts_light.png b/ultrasonic/src/main/res/drawable-hdpi/ic_menu_podcasts_light.png new file mode 100644 index 00000000..c3b1ae6e Binary files /dev/null and b/ultrasonic/src/main/res/drawable-hdpi/ic_menu_podcasts_light.png differ diff --git a/ultrasonic/src/main/res/drawable-mdpi/ic_menu_podcasts_dark.png b/ultrasonic/src/main/res/drawable-mdpi/ic_menu_podcasts_dark.png new file mode 100644 index 00000000..3e8e06de Binary files /dev/null and b/ultrasonic/src/main/res/drawable-mdpi/ic_menu_podcasts_dark.png differ diff --git a/ultrasonic/src/main/res/drawable-mdpi/ic_menu_podcasts_light.png b/ultrasonic/src/main/res/drawable-mdpi/ic_menu_podcasts_light.png new file mode 100644 index 00000000..04b43f5b Binary files /dev/null and b/ultrasonic/src/main/res/drawable-mdpi/ic_menu_podcasts_light.png differ diff --git a/ultrasonic/src/main/res/drawable-xhdpi/ic_menu_podcasts_dark.png b/ultrasonic/src/main/res/drawable-xhdpi/ic_menu_podcasts_dark.png new file mode 100644 index 00000000..e6905dc5 Binary files /dev/null and b/ultrasonic/src/main/res/drawable-xhdpi/ic_menu_podcasts_dark.png differ diff --git a/ultrasonic/src/main/res/drawable-xhdpi/ic_menu_podcasts_light.png b/ultrasonic/src/main/res/drawable-xhdpi/ic_menu_podcasts_light.png new file mode 100644 index 00000000..a4d24914 Binary files /dev/null and b/ultrasonic/src/main/res/drawable-xhdpi/ic_menu_podcasts_light.png differ diff --git a/ultrasonic/src/main/res/drawable-xxhdpi/ic_menu_podcasts_dark.png b/ultrasonic/src/main/res/drawable-xxhdpi/ic_menu_podcasts_dark.png new file mode 100644 index 00000000..79dcc4b6 Binary files /dev/null and b/ultrasonic/src/main/res/drawable-xxhdpi/ic_menu_podcasts_dark.png differ diff --git a/ultrasonic/src/main/res/drawable-xxhdpi/ic_menu_podcasts_light.png b/ultrasonic/src/main/res/drawable-xxhdpi/ic_menu_podcasts_light.png new file mode 100644 index 00000000..1ed902a6 Binary files /dev/null and b/ultrasonic/src/main/res/drawable-xxhdpi/ic_menu_podcasts_light.png differ diff --git a/ultrasonic/src/main/res/layout/menu_main.xml b/ultrasonic/src/main/res/layout/menu_main.xml index 391feb57..481ecd8e 100644 --- a/ultrasonic/src/main/res/layout/menu_main.xml +++ b/ultrasonic/src/main/res/layout/menu_main.xml @@ -81,6 +81,14 @@ android:drawableLeft="?attr/media_play" android:text="@string/button_bar.now_playing"/> + + + + + + + + + + + + + \ No newline at end of file diff --git a/ultrasonic/src/main/res/layout/podcasts_channel_item.xml b/ultrasonic/src/main/res/layout/podcasts_channel_item.xml new file mode 100644 index 00000000..9b8560e8 --- /dev/null +++ b/ultrasonic/src/main/res/layout/podcasts_channel_item.xml @@ -0,0 +1,9 @@ + + + + diff --git a/ultrasonic/src/main/res/values/strings.xml b/ultrasonic/src/main/res/values/strings.xml index 9a142f56..9249b973 100644 --- a/ultrasonic/src/main/res/values/strings.xml +++ b/ultrasonic/src/main/res/values/strings.xml @@ -12,6 +12,9 @@ Chat UltraSonic Main Now Playing + Podcast + No podcasts channels registered + Podcast Playlists Search Send a message diff --git a/ultrasonic/src/main/res/values/styles.xml b/ultrasonic/src/main/res/values/styles.xml index 4c10b38e..f8589503 100644 --- a/ultrasonic/src/main/res/values/styles.xml +++ b/ultrasonic/src/main/res/values/styles.xml @@ -80,6 +80,7 @@ + diff --git a/ultrasonic/src/main/res/values/themes.xml b/ultrasonic/src/main/res/values/themes.xml index f6302ced..0c3dae13 100644 --- a/ultrasonic/src/main/res/values/themes.xml +++ b/ultrasonic/src/main/res/values/themes.xml @@ -35,6 +35,7 @@ @drawable/media_repeat_single @drawable/media_shuffle_normal_dark @drawable/media_start_normal_dark + @drawable/ic_menu_podcasts_dark @drawable/media_play_next @drawable/ic_stat_play_dark @drawable/media_stop_normal_dark @@ -79,6 +80,7 @@ @drawable/media_repeat_single @drawable/media_shuffle_normal_light @drawable/media_start_normal_light + @drawable/ic_menu_podcasts_light @drawable/media_play_next @drawable/ic_stat_play_light @drawable/media_stop_normal_light