/src/br/com/carlosrafaelgn/fplay/list/RadioStationList.java
Java | 832 lines | 745 code | 35 blank | 52 comment | 215 complexity | a732e7fd4b387c406b29dd45b571d574 MD5 | raw file
- //
- // FPlayAndroid is distributed under the FreeBSD License
- //
- // Copyright (c) 2013-2014, Carlos Rafael Gimenes das Neves
- // All rights reserved.
- //
- // Redistribution and use in source and binary forms, with or without
- // modification, are permitted provided that the following conditions are met:
- //
- // 1. Redistributions of source code must retain the above copyright notice, this
- // list of conditions and the following disclaimer.
- // 2. Redistributions in binary form must reproduce the above copyright notice,
- // this list of conditions and the following disclaimer in the documentation
- // and/or other materials provided with the distribution.
- //
- // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
- // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
- // ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- //
- // The views and conclusions contained in the software and documentation are those
- // of the authors and should not be interpreted as representing official policies,
- // either expressed or implied, of the FreeBSD Project.
- //
- // https://github.com/carlosrafaelgn/FPlayAndroid
- //
- package br.com.carlosrafaelgn.fplay.list;
- import android.content.Context;
- import android.os.Message;
- import android.view.View;
- import android.view.ViewGroup;
- import org.xmlpull.v1.XmlPullParser;
- import org.xmlpull.v1.XmlPullParserFactory;
- import java.io.BufferedInputStream;
- import java.io.BufferedOutputStream;
- import java.io.FileInputStream;
- import java.io.FileNotFoundException;
- import java.io.FileOutputStream;
- import java.io.IOException;
- import java.io.InputStream;
- import java.net.HttpURLConnection;
- import java.net.URL;
- import java.net.URLEncoder;
- import java.util.HashSet;
- import br.com.carlosrafaelgn.fplay.R;
- import br.com.carlosrafaelgn.fplay.activity.MainHandler;
- import br.com.carlosrafaelgn.fplay.playback.Player;
- import br.com.carlosrafaelgn.fplay.ui.RadioStationView;
- import br.com.carlosrafaelgn.fplay.ui.UI;
- import br.com.carlosrafaelgn.fplay.util.ArraySorter;
- import br.com.carlosrafaelgn.fplay.util.Serializer;
- public final class RadioStationList extends BaseList<RadioStation> implements Runnable, ArraySorter.Comparer<RadioStation>, MainHandler.Callback {
- public interface RadioStationAddedObserver {
- void onRadioStationAdded();
- }
- //after analyzing the results obtained from http://dir.xiph.org/xxx
- //I noticed that there are never more than 5 pages of results,
- //with 20 results each ;)
- private static final int MAX_COUNT = 100;
- private static final int MSG_FINISHED = 0x0300;
- private static final int MSG_MORE_RESULTS = 0x0301;
-
- //public static final int POPULAR_GENRE_COUNT = 32;
- //I took these genres from http://dir.xiph.org/yp.xml
- //... after grouping, counting, sorting and selecting properly ;)
- public static final String[] GENRES = new String[] {
- "8bit",
- "Alternative",
- "Anime",
- "Christian",
- "Classic",
- "Classical",
- "Dance",
- "Disco",
- "Electronic",
- "Hits",
- "House",
- "Jazz",
- "Lounge",
- "Metal",
- "Misc",
- "Music",
- "News",
- "Oldies",
- "Pop",
- "Radio",
- "Reggae",
- "Rock",
- "Salsa",
- "Ska",
- "Talk",
- "Techno",
- "Top",
- "Top40",
- "Top100",
- "Trance",
- "Various",
- "Video Game", //last popular genre
-
- "40s",
- "50s",
- "60s",
- "70s",
- "80s",
- "90s",
- "00s",
- "Adult",
- "Alternate",
- "Ambiance",
- "Ambient",
- "Argentina",
- "Baladas",
- "Bass",
- "Beatles",
- "Bible",
- "Blues",
- "Broadway",
- "Catholic",
- "Celtic",
- "Chill",
- "Chillout",
- "Chiptunes",
- "Club",
- "Comedy",
- "Contemporary",
- "Country",
- "Downtempo",
- "Dubstep",
- "Easy",
- "Eclectic",
- "Electro",
- "Electronica",
- "Elektro",
- "Eurodance",
- "Experimental",
- "Folk",
- "France",
- "Funk",
- "German",
- "Gospel",
- "Goth",
- "Hardcore",
- "Hardstyle",
- "Hindi",
- "Hiphop",
- "Hit",
- "Ibiza",
- "Indie",
- "Industrial",
- "Inspirational",
- "Instrumental",
- "International",
- "Italia",
- "Japan",
- "Jpop",
- "Jrock",
- "Jungle",
- "Korea",
- "Kpop",
- "Latin",
- "Latina",
- "Latinpop",
- "Layback",
- "Libre",
- "Live",
- "Lovesongs",
- "Mariachi",
- "Mashup",
- "Merengue",
- "Minecraft",
- "Mixed",
- "Modern",
- "Motown",
- "Mozart",
- "Musica",
- "Nederlands",
- "New",
- "Oldschool",
- "Paris",
- "Progressive",
- "Psytrance",
- "Punk",
- "Punkrock",
- "Rap",
- "Recuerdos",
- "Reggaeton",
- "Relax",
- "Remixes",
- "Rockabilly",
- "Romantica",
- "Roots",
- "Russian",
- "Schlager",
- "Sertanejo",
- "Slow",
- "Smooth",
- "Soul",
- "Soundtrack",
- "Southern",
- "Sports",
- "Student",
- "Tech",
- "Tropical",
- "Webradio",
- "Western",
- "World",
- "Zen",
- "Zouk"
- };
-
- private boolean loading, favoritesLoaded, favoritesChanged;
- private final Object favoritesSync;
- private final HashSet<RadioStation> favorites;
- private final String tags, noOnAir, noDescription, noTags;
- private volatile boolean readyToFetch, isSavingFavorites;
- private volatile int version;
- private volatile String genreToFetch, searchTermToFetch;
- private volatile Context context;
- public RadioStationAddedObserver radioStationAddedObserver;
-
- public RadioStationList(String tags, String noOnAir, String noDescription, String noTags) {
- super(RadioStation.class, MAX_COUNT);
- this.items = new RadioStation[MAX_COUNT];
- this.readyToFetch = true;
- this.favoritesSync = new Object();
- this.favorites = new HashSet<>(32);
- this.tags = tags;
- this.noOnAir = noOnAir;
- this.noDescription = noDescription;
- this.noTags = noTags;
- }
-
- public boolean isLoading() {
- return loading;
- }
-
- private void loadingProcessChanged(boolean started) {
- loading = started;
- if (UI.browserActivity != null)
- UI.browserActivity.loadingProcessChanged(started);
- }
-
- public void cancel() {
- version++;
- if (loading)
- loadingProcessChanged(false);
- }
-
- private static String readStringIfPossible(XmlPullParser parser, StringBuilder sb) throws Throwable {
- sb.delete(0, sb.length());
- switch (parser.getEventType()) {
- case XmlPullParser.COMMENT:
- break;
- case XmlPullParser.ENTITY_REF:
- break;
- case XmlPullParser.IGNORABLE_WHITESPACE:
- sb.append(' ');
- break;
- case XmlPullParser.PROCESSING_INSTRUCTION:
- case XmlPullParser.TEXT:
- if (parser.isWhitespace())
- sb.append(' ');
- else
- sb.append(parser.getText());
- break;
- default:
- return null;
- }
- for (; ; ) {
- switch (parser.nextToken()) {
- case XmlPullParser.COMMENT:
- break;
- case XmlPullParser.ENTITY_REF:
- break;
- case XmlPullParser.IGNORABLE_WHITESPACE:
- sb.append(' ');
- break;
- case XmlPullParser.PROCESSING_INSTRUCTION:
- case XmlPullParser.TEXT:
- if (parser.isWhitespace())
- sb.append(' ');
- else
- sb.append(parser.getText());
- break;
- default:
- return sb.toString();
- }
- }
- }
-
- private static boolean parseIcecastColumn2(XmlPullParser parser, String[] fields) throws Throwable {
- boolean hasFields = false, linkContainsType = false;
- int ev;
- String v;
- while ((ev = parser.nextToken()) != XmlPullParser.END_DOCUMENT) {
- if (ev == XmlPullParser.END_TAG && parser.getName().equals("td"))
- break;
- if (ev == XmlPullParser.START_TAG) {
- if (parser.getName().equals("p") && hasFields) {
- linkContainsType = true;
- } else if (parser.getName().equals("a")) {
- if (linkContainsType) {
- if (parser.nextToken() != XmlPullParser.TEXT) {
- //impossible to determine the type of the stream...
- //just drop it!
- hasFields = false;
- } else {
- v = parser.getText().trim();
- hasFields = (v.equals("MP3") || v.equals("Ogg Vorbis"));
- fields[2] = v;
- }
- } else {
- for (int a = parser.getAttributeCount() - 1; a >= 0; a--) {
- if (parser.getAttributeName(a).equals("href") &&
- (v = parser.getAttributeValue(a)).endsWith("m3u")) {
- fields[7] = ((v.charAt(0) == '/') ? ("http://dir.xiph.org" + v) : (v)).trim();
- hasFields = true;
- break;
- }
- }
- }
- }
- }
- }
- return hasFields;
- }
-
- private boolean parseIcecastColumn1(XmlPullParser parser, String[] fields, StringBuilder sb) throws Throwable {
- int ev = 0, pCount = 0;
- boolean hasFields = false, hasNextToken = false, parsingTags = false;
- String str;
- while (hasNextToken || ((ev = parser.nextToken()) != XmlPullParser.END_DOCUMENT)) {
- hasNextToken = false;
- if (ev == XmlPullParser.END_TAG && parser.getName().equals("td"))
- break;
- if (ev == XmlPullParser.START_TAG && parser.getName().equals("p")) {
- pCount++;
- } else if (ev == XmlPullParser.START_TAG && parser.getName().equals("ul")) {
- parsingTags = true;
- sb.delete(0, sb.length());
- } else if (parsingTags) {
- if (ev == XmlPullParser.START_TAG && parser.getName().equals("a")) {
- if (parser.nextToken() == XmlPullParser.TEXT) {
- if (sb.length() > 0) {
- sb.append(' ');
- } else {
- sb.append(tags);
- sb.append(": ");
- }
- sb.append(parser.getText());
- } else {
- hasNextToken = true;
- ev = parser.getEventType();
- }
- } else if (ev == XmlPullParser.END_TAG && parser.getName().equals("ul")) {
- hasFields = true;
- fields[6] = sb.toString().trim();
- }
- } else {
- switch (pCount) {
- case 1:
- if (ev == XmlPullParser.START_TAG) {
- if (parser.getName().equals("a")) {
- for (int a = parser.getAttributeCount() - 1; a >= 0; a--) {
- if (parser.getAttributeName(a).equals("href")) {
- fields[1] = parser.getAttributeValue(a).trim();
- //set hasFields to true, only if the title has been found!
- //hasFields = true;
- break;
- }
- }
- parser.nextToken();
- if ((str = readStringIfPossible(parser, sb)) != null) {
- hasFields = true;
- fields[0] = str.trim();
- }
- hasNextToken = true;
- ev = parser.getEventType();
- } else if (fields[0].length() != 0 && parser.getName().equals("span")) {
- if (parser.nextToken() == XmlPullParser.TEXT) {
- fields[3] = parser.getText().trim();
- if (fields[3].length() > 0)
- fields[3] = fields[3].substring(1).trim();
- } else {
- hasNextToken = true;
- ev = parser.getEventType();
- }
- }
- }
- break;
- case 2:
- if (fields[4].length() == 0 && (str = readStringIfPossible(parser, sb)) != null) {
- hasFields = true;
- fields[4] = str.trim();
- hasNextToken = true;
- ev = parser.getEventType();
- } else {
- hasNextToken = false;
- }
- break;
- case 3:
- if (ev == XmlPullParser.END_TAG && parser.getName().equals("strong")) {
- if (fields[5].length() == 0) {
- parser.nextToken();
- if ((str = readStringIfPossible(parser, sb)) != null) {
- hasFields = true;
- fields[5] = str.trim();
- }
- hasNextToken = true;
- ev = parser.getEventType();
- }
- }
- break;
- }
- }
- }
- return hasFields;
- }
-
- private boolean parseIcecastRow(XmlPullParser parser, String[] fields, StringBuilder sb) throws Throwable {
- fields[0] = ""; //title
- fields[1] = ""; //uri
- fields[2] = ""; //type
- fields[3] = ""; //listeners
- fields[4] = ""; //description
- fields[5] = ""; //onAir
- fields[6] = ""; //tags
- fields[7] = ""; //m3uUri
- int ev, colCount = 0;
- while ((ev = parser.nextToken()) != XmlPullParser.END_DOCUMENT && colCount < 2) {
- if (ev == XmlPullParser.END_TAG && parser.getName().equals("tr"))
- break;
- if (ev == XmlPullParser.START_TAG && parser.getName().equals("td")) {
- colCount++;
- if (colCount == 1) {
- if (!parseIcecastColumn1(parser, fields, sb))
- return false;
- } else {
- if (!parseIcecastColumn2(parser, fields))
- return false;
- }
- }
- }
- return true;
- }
-
- private boolean parseIcecastResults(InputStream is, String[] fields, int myVersion, StringBuilder sb, int[] currentStationIndex) throws Throwable {
- int b = 0;
- while (b >= 0) {
- if ((b = is.read()) == (int)'<' &&
- (b = is.read()) == (int)'h' &&
- (b = is.read()) == (int)'2' &&
- (b = is.read()) == (int)'>')
- break;
- }
- if (b < 0)
- return false;
- while (b >= 0) {
- if ((b = is.read()) == (int)'<' &&
- (b = is.read()) == (int)'/' &&
- (b = is.read()) == (int)'h' &&
- (b = is.read()) == (int)'2' &&
- (b = is.read()) == (int)'>')
- break;
- }
- if (b < 0)
- return false;
- boolean hasResults = false;
- //According to these docs, kXML parser will accept some XML documents
- //that should actually be rejected (A robust "relaxed" mode for parsing
- //HTML or SGML files):
- //http://developer.android.com/training/basics/network-ops/xml.html
- //http://kxml.org/index.html
- try {
- XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
- XmlPullParser parser = factory.newPullParser();
- parser.setInput(is, "UTF-8");
- //special feature! (check out kXML2 source and you will find it!)
- parser.setFeature("http://xmlpull.org/v1/doc/features.html#relaxed", true);
- int ev;
- while ((ev = parser.nextToken()) != XmlPullParser.END_DOCUMENT && currentStationIndex[0] < MAX_COUNT) {
- if (ev == XmlPullParser.END_TAG && parser.getName().equals("table"))
- break;
- if (ev == XmlPullParser.START_TAG && parser.getName().equals("tr")) {
- if (myVersion != version)
- break;
- if (parseIcecastRow(parser, fields, sb) && myVersion == version) {
- final RadioStation station = new RadioStation(fields[0], fields[1], fields[2], fields[4].length() == 0 ? noDescription : fields[4], fields[5].length() == 0 ? noOnAir : fields[5], fields[6].length() == 0 ? noTags : fields[6], fields[7], false);
- synchronized (favoritesSync) {
- station.isFavorite = favorites.contains(station);
- }
- if (myVersion != version)
- break;
- items[currentStationIndex[0]++] = station;
- hasResults = true;
- }
- }
- }
- } catch (Throwable ex) {
- ex.printStackTrace();
- }
- return hasResults;
- }
-
- private void loadFavoritesInternal(Context context) throws IOException {
- FileInputStream fs = null;
- BufferedInputStream bs = null;
- try {
- fs = context.openFileInput("_RadioFav");
- bs = new BufferedInputStream(fs, 4096);
- final int version = Serializer.deserializeInt(bs);
- final int count = Math.min(Serializer.deserializeInt(bs), MAX_COUNT);
- if (version == 0x0100 && count > 0) {
- favorites.clear();
- for (int i = 0; i < count; i++)
- favorites.add(RadioStation.deserialize(bs, true));
- }
- } catch (IOException ex) {
- if (ex instanceof FileNotFoundException && fs == null) {
- favorites.clear();
- } else {
- throw ex;
- }
- } finally {
- try {
- if (bs != null)
- bs.close();
- } catch (Throwable ex) {
- ex.printStackTrace();
- }
- try {
- if (fs != null)
- fs.close();
- } catch (Throwable ex) {
- ex.printStackTrace();
- }
- }
- }
-
- private void saveFavoritesInternal(Context context) throws IOException {
- FileOutputStream fs = null;
- BufferedOutputStream bs = null;
- try {
- final int count = Math.min(MAX_COUNT, favorites.size());
- int i = 0;
- fs = context.openFileOutput("_RadioFav", 0);
- bs = new BufferedOutputStream(fs, 4096);
- Serializer.serializeInt(bs, 0x0100);
- Serializer.serializeInt(bs, count);
- for (RadioStation s : favorites) {
- if (i >= count)
- break;
- s.serialize(bs);
- i++;
- }
- bs.flush();
- } finally {
- try {
- if (bs != null)
- bs.close();
- } catch (Throwable ex) {
- ex.printStackTrace();
- }
- try {
- if (fs != null)
- fs.close();
- } catch (Throwable ex) {
- ex.printStackTrace();
- }
- }
- }
-
- @Override
- public int compare(RadioStation a, RadioStation b) {
- int r = a.title.compareToIgnoreCase(b.title);
- if (r != 0)
- return r;
- r = a.onAir.compareToIgnoreCase(b.onAir);
- if (r != 0)
- return r;
- return a.m3uUri.compareTo(b.m3uUri);
- }
-
- @Override
- public void run() {
- final int myVersion = version;
- final Context context = this.context;
- final String genre = genreToFetch, searchTerm = searchTermToFetch;
- final boolean isSavingFavorites = this.isSavingFavorites;
- this.context = null;
- readyToFetch = true;
-
- int err = 0;
-
- if (!favoritesLoaded && !isSavingFavorites && context != null) {
- synchronized (favoritesSync) {
- if (!favoritesLoaded) {
- try {
- loadFavoritesInternal(context);
- favoritesLoaded = true;
- favoritesChanged = false;
- } catch (Throwable ex) {
- ex.printStackTrace();
- }
- }
- }
- }
-
- if (genre == null && searchTerm == null) {
- //favorites
- synchronized (favoritesSync) {
- if (isSavingFavorites) {
- try {
- if (favoritesLoaded && favoritesChanged && context != null) {
- saveFavoritesInternal(context);
- favoritesChanged = false;
- }
- } catch (Throwable ex) {
- MainHandler.toast(R.string.error_gen);
- }
- } else {
- try {
- if (favoritesLoaded && context != null) {
- if (myVersion != version)
- return;
- final RadioStation[] stations = new RadioStation[favorites.size()];
- favorites.toArray(stations);
- ArraySorter.sort(stations, 0, stations.length, this);
- if (myVersion == version) {
- final int count = Math.min(stations.length, MAX_COUNT);
- System.arraycopy(stations, 0, items, 0, count);
- MainHandler.sendMessage(RadioStationList.this, MSG_MORE_RESULTS, myVersion, count);
- }
- }
- } catch (Throwable ex) {
- err = -2;
- } finally {
- if (myVersion == version)
- MainHandler.sendMessage(RadioStationList.this, MSG_FINISHED, myVersion, err);
- }
- }
- }
- return;
- }
-
- try {
- int pageNumber = 0;
- boolean hasResults;
- String[] fields = new String[8];
- final StringBuilder sb = new StringBuilder(256);
- final int[] currentStationIndex = { 0 };
-
- //genre MUST be one of the predefined genres (due to the encoding)
- final String uri = ((genre != null) ?
- ("http://dir.xiph.org/by_genre/" + genre.replace(" ", "%20") + "?page=") :
- ("http://dir.xiph.org/search?search=" + URLEncoder.encode(searchTerm, "UTF-8") + "&page="));
- do {
- if (myVersion != version)
- break;
- InputStream is = null;
- HttpURLConnection urlConnection = null;
- try {
- urlConnection = (HttpURLConnection)(new URL(uri + pageNumber)).openConnection();
- if (myVersion != version)
- break;
- err = urlConnection.getResponseCode();
- if (err == 200) {
- is = urlConnection.getInputStream();
- hasResults = parseIcecastResults(is, fields, myVersion, sb, currentStationIndex);
- if (hasResults && myVersion == version)
- MainHandler.sendMessage(RadioStationList.this, MSG_MORE_RESULTS, myVersion, currentStationIndex[0]);
- err = 0;
- } else {
- hasResults = false;
- }
- } catch (Throwable ex) {
- hasResults = false;
- err = -1;
- } finally {
- try {
- if (urlConnection != null)
- urlConnection.disconnect();
- } catch (Throwable ex) {
- ex.printStackTrace();
- }
- try {
- if (is != null)
- is.close();
- } catch (Throwable ex) {
- ex.printStackTrace();
- }
- System.gc();
- }
- pageNumber++;
- } while (hasResults && pageNumber < 5);
- } catch (Throwable ex) {
- err = -1;
- } finally {
- if (myVersion == version)
- MainHandler.sendMessage(RadioStationList.this, MSG_FINISHED, myVersion, err);
- }
- }
-
- public void addFavoriteStation(RadioStation station) {
- synchronized (favoritesSync) {
- if (favoritesLoaded) {
- station.isFavorite = true;
- favoritesChanged |= favorites.add(station);
- }
- }
- }
-
- public void removeFavoriteStation(RadioStation station) {
- synchronized (favoritesSync) {
- if (favoritesLoaded) {
- station.isFavorite = false;
- favoritesChanged |= favorites.remove(station);
- }
- }
- }
-
- public void fetchIcecast(Context context, String genre, String searchTerm) {
- while (!readyToFetch)
- Thread.yield();
- cancel();
- clear();
- loadingProcessChanged(true);
- final Thread t = new Thread(this, "Icecast Station Fetcher Thread");
- isSavingFavorites = false;
- genreToFetch = genre;
- searchTermToFetch = searchTerm;
- this.context = context;
- readyToFetch = false;
- try {
- t.start();
- } catch (Throwable ex) {
- readyToFetch = true;
- loadingProcessChanged(false);
- }
- }
-
- public void fetchFavorites(Context context) {
- while (!readyToFetch)
- Thread.yield();
- cancel();
- clear();
- loadingProcessChanged(true);
- final Thread t = new Thread(this, "Icecast Favorite Stations Fetcher Thread");
- isSavingFavorites = false;
- genreToFetch = null;
- searchTermToFetch = null;
- this.context = context;
- readyToFetch = false;
- try {
- t.start();
- } catch (Throwable ex) {
- readyToFetch = true;
- loadingProcessChanged(false);
- }
- }
-
- public void saveFavorites(Context context) {
- while (!readyToFetch)
- Thread.yield();
- synchronized (favoritesSync) {
- if (!favoritesLoaded || !favoritesChanged)
- return;
- }
- final Thread t = new Thread(this, "Icecast Favorite Stations Storer Thread");
- isSavingFavorites = true;
- genreToFetch = null;
- searchTermToFetch = null;
- this.context = context;
- readyToFetch = false;
- try {
- t.start();
- } catch (Throwable ex) {
- readyToFetch = true;
- }
- }
-
- @Override
- public boolean handleMessage(Message msg) {
- if (msg.arg1 != version)
- return true;
- switch (msg.what) {
- case MSG_FINISHED:
- loadingProcessChanged(false);
- if (msg.arg2 != 0)
- UI.toast(Player.getService(), ((msg.arg2 != -2) && !Player.isConnectedToTheInternet()) ? R.string.error_connection : R.string.error_gen);
- break;
- case MSG_MORE_RESULTS:
- //protection against out of order messages... does this really happen? ;)
- if (msg.arg2 > count) {
- //items are always appended :)
- modificationVersion++;
- final int c = count;
- count = msg.arg2;
- addingItems(c, c - count);
- notifyDataSetChanged(-1, CONTENT_ADDED);
- if (radioStationAddedObserver != null)
- radioStationAddedObserver.onRadioStationAdded();
- }
- break;
- }
- return true;
- }
-
- @Override
- public View getView(int position, View convertView, ViewGroup parent) {
- final RadioStationView view = ((convertView != null) ? (RadioStationView)convertView : new RadioStationView(Player.getService()));
- view.setItemState(items[position], position, getItemState(position));
- return view;
- }
-
- @Override
- public int getViewHeight() {
- return RadioStationView.getViewHeight();
- }
- }