diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1ad105e --- /dev/null +++ b/.gitignore @@ -0,0 +1,10 @@ + ++*.iml ++.gradle ++/local.properties ++/.idea ++.DS_Store ++/build ++/captures ++gradle ++gradlew diff --git a/COPYING b/LICENSE similarity index 99% rename from COPYING rename to LICENSE index 94a9ed0..733c072 100644 --- a/COPYING +++ b/LICENSE @@ -631,8 +631,8 @@ to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. - - Copyright (C) + {one line to give the program's name and a brief idea of what it does.} + Copyright (C) {year} {name of author} This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -652,7 +652,7 @@ Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: - Copyright (C) + {project} Copyright (C) {year} {fullname} This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. @@ -672,3 +672,4 @@ may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read . + diff --git a/README.md b/README.md index 1ded5d6..f30a0b0 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,14 @@ Nectroid ======== Nectroid provides a way to access -[Nectarine Demoscene Radio](https://www.scenemusic.net/demovibes/) from Android +[Nectarine Demoscene Radio](https://www.scenemusic.net/demovibes/) or [CVGM Radio](http://www.cvgm.net/demovibes) from Android devices. Current feature set: * Background music streaming - * Current song, OneLiner, History and Queue views + * Login and logout (scenemusic, CVGM or Rock with Wolfenstein account needed) + * Current song, OneLiner, History, Queue and Favorites views + * Submit song from your favorites + * Submit message to oneliner * AudioScrobbler support * Support for other Demovibes-powered sites like [CVGM](http://www.cvgm.net/demovibes/) @@ -13,6 +16,7 @@ devices. Current feature set: Nectroid source code (C) 2010, 2012 Kevin Vance +(C) 2016 Decorde Matthieu Distribute under the terms of the GNU GPL v3. diff --git a/ant.properties b/ant.properties deleted file mode 100644 index ee52d86..0000000 --- a/ant.properties +++ /dev/null @@ -1,17 +0,0 @@ -# This file is used to override default values used by the Ant build system. -# -# This file must be checked in Version Control Systems, as it is -# integral to the build system of your project. - -# This file is only used by the Ant script. - -# You can use this to override default values such as -# 'source.dir' for the location of your java source folder and -# 'out.dir' for the location of your output folder. - -# You can also use it define how the release builds are signed by declaring -# the following properties: -# 'key.store' for the location of your keystore and -# 'key.alias' for the name of the key to use. -# The password will be asked during the build when you use the 'release' target. - diff --git a/app-debug.apk b/app-debug.apk new file mode 100644 index 0000000..0100cc8 Binary files /dev/null and b/app-debug.apk differ diff --git a/app/.gitignore b/app/.gitignore new file mode 100644 index 0000000..796b96d --- /dev/null +++ b/app/.gitignore @@ -0,0 +1 @@ +/build diff --git a/app/app.iml b/app/app.iml new file mode 100644 index 0000000..62f840d --- /dev/null +++ b/app/app.iml @@ -0,0 +1,113 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle new file mode 100644 index 0000000..f493456 --- /dev/null +++ b/app/build.gradle @@ -0,0 +1,24 @@ +apply plugin: 'com.android.application' + +android { + compileSdkVersion 21 + buildToolsVersion "23.0.3" + + defaultConfig { + applicationId "com.kvance.Nectroid" + minSdkVersion 3 + targetSdkVersion 4 + versionCode 12 + versionName "1.3" + ndk { + moduleName "libmad" + } + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt' + } + } +} diff --git a/AndroidManifest.xml b/app/src/main/AndroidManifest.xml similarity index 93% rename from AndroidManifest.xml rename to app/src/main/AndroidManifest.xml index 3887244..a007f7b 100644 --- a/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,8 +1,8 @@ + android:versionCode="12" + android:versionName="1.3"> - + . + +package com.kvance.Nectroid; + +import android.content.Context; +import android.sax.Element; +import android.sax.EndElementListener; +import android.sax.EndTextElementListener; +import android.sax.RootElement; +import android.sax.StartElementListener; +import android.sax.TextElementListener; +import android.util.Log; +import android.util.Xml; + +import org.xml.sax.Attributes; +import org.xml.sax.SAXException; + +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.Date; +import java.util.List; + + +/** A playlist has a current song, a history, and an upcoming list. */ +class Favorites +{ + /** A single song in a playlist */ + public class Entry { + protected int mId = 0; + protected String mTitle = new String("UNKNOWN"); + protected Boolean mLocked = false; + + public int getId() { return mId; } + public String getTitle() { return mTitle; } + public boolean isLocked() { return mLocked; } + + @Override + public String toString() { + return String.format( + "", + mTitle, mId, mLocked.toString()); + } + } + + List mFavorites; + + private static final String TAG = "NectroidPlaylist"; + + + public Favorites() { + mFavorites = new ArrayList(); + } + + public Favorites(String xmlData) throws SAXException { + this(); + new Parser().parse(xmlData); + } + + /// + /// Getters + /// + + public List getFavorites() { return mFavorites; } + + /// + /// XML parser + /// + + private class Parser { + + /** Parse value into an int, or return -1. + * + * @param value the string to parse as an integer + * @param description a description of the string to print in an error message + */ + private int parseIntOrMinusOne(String value, String description) + { + // Same code in Stream XML parser + return Stream.Parser.parseIntOrMinusOne(value, description, TAG); + } + + private class SongTextListener implements TextElementListener + { + private int mId = -1; + private Boolean mLocked = false; + + public void start(Attributes attributes) { + + String idString = attributes.getValue("id"); + mId = parseIntOrMinusOne(idString, "song id"); + + String locked = attributes.getValue("locked"); + mLocked = "True".equals(locked); + } + + public void end(String body) { + Entry mNewEntry = new Entry(); + mNewEntry.mId = mId; + mNewEntry.mLocked = mLocked; + mNewEntry.mTitle = body; + if (!mNewEntry.mLocked) + mFavorites.add(mNewEntry); + } + } + + public void parse(String xmlData) throws SAXException + { + RootElement root = new RootElement("favorites"); + + Element song = root.getChild("song"); + SongTextListener songTextListener = new SongTextListener(); + song.setTextElementListener(songTextListener); + + Xml.parse(xmlData, root.getContentHandler()); + } + } +} diff --git a/app/src/main/java/com/kvance/Nectroid/FavoritesActivity.java b/app/src/main/java/com/kvance/Nectroid/FavoritesActivity.java new file mode 100644 index 0000000..b11d07a --- /dev/null +++ b/app/src/main/java/com/kvance/Nectroid/FavoritesActivity.java @@ -0,0 +1,256 @@ +// This file is part of Nectroid. +// +// Nectroid 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. +// +// Nectroid 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 Nectroid. If not, see . + +package com.kvance.Nectroid; + +import android.app.ListActivity; +import android.content.Intent; +import android.net.Uri; +import android.os.AsyncTask; +import android.os.Bundle; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; +import android.view.View; +import android.view.Window; +import android.widget.ListView; +import android.widget.TextView; +import android.widget.Toast; + +import org.apache.http.HttpResponse; +import org.apache.http.NameValuePair; +import org.apache.http.client.HttpClient; +import org.apache.http.client.entity.UrlEncodedFormEntity; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.message.BasicNameValuePair; + +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.net.URL; +import java.util.ArrayList; +import java.util.List; + +public class FavoritesActivity extends ListActivity + implements BackgroundTaskListener +{ + + private FavoritesManager mFavoritesManager; + + private Favorites mFavorites; + private FavoritesAdapter mListAdapter; + + private static final String TAG = "Nectroid"; + + + /// + /// Activity event handlers + /// + + @Override + public void onCreate(Bundle savedInstanceState) + { + super.onCreate(savedInstanceState); + requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS); + setContentView(R.layout.inset_list); + + ((NectroidApplication)getApplication()).updateWindowBackground(getWindow()); + + // Check for a data URI + Uri dataUri = getIntent().getData(); + if(dataUri == null) { + throw new RuntimeException("Started FavoritesActivity with null data URI"); + } + if(!dataUri.getScheme().equals("nectroid")) { + throw new RuntimeException(String.format("Unknown URI scheme \"%s\" in %s", + dataUri.getScheme(), dataUri)); + } + + // Check which favorites we're supposed to display + String favoritesName = dataUri.getSchemeSpecificPart(); + if(!favoritesName.equals("//favorites")) { + throw new RuntimeException(String.format("Unknown favoritesName \"%s\" requested in %s", + favoritesName, dataUri)); + } + } + + @Override + public void onStart() + { + super.onStart(); + + // Subscribe to playlist events. + NectroidApplication app = (NectroidApplication)getApplication(); + mFavoritesManager = app.getFavoritesManager(); + mFavoritesManager.addTaskListener(this); + + // Set up the UI for whichever playlist was requested + mFavorites = mFavoritesManager.getFavorites(); + String appName = getString(R.string.app_name); + String listName = "Favorites"; + int emptyText = R.string.nofavs; + mListAdapter = new FavoritesAdapter(mFavorites, this); + setListAdapter(mListAdapter); + + setTitle(String.format("%s - %s", appName, listName)); + TextView emptyView = (TextView)findViewById(android.R.id.empty); + emptyView.setText(emptyText); + + // Update the throbber to the current state. (If we do this before we set the title, it + // seems to be ignored.) + setProgressBarIndeterminateVisibility(mFavoritesManager.isUpdating()); + } + + @Override + public void onStop() + { + // Unsubscribe from updates. + mFavoritesManager.removeTaskListener(this); + + super.onStop(); + } + + @Override + public void onResume() + { + super.onResume(); + mFavoritesManager.requestAutoRefresh(this); + } + + @Override + public void onPause() + { + super.onPause(); + mFavoritesManager.unrequestAutoRefresh(this); + } + + + @Override + protected void onListItemClick(ListView l, final View v, int position, long id) + { + // Find the base URL for the current site. + NectroidApplication app = (NectroidApplication)getApplication(); + String baseUrl = app.getSiteManager().getCurrentSite().getBaseUrl(); + + // Open the song link. + Favorites.Entry song = (Favorites.Entry)mListAdapter.getItem(position); + + // Req the song is not locked + if (song.isLocked()) { + Toast.makeText(this, "The song already locked :(", Toast.LENGTH_LONG).show(); + } else { + + try { + String url = baseUrl + FavoritesActivity.this.getString(R.string.url_song_queue); + HttpClient client = app.getHttpClient(); + + HttpPost post = new HttpPost(url); + System.out.println("SONG QUEUE URL="+url); + List nameValuePairs = new ArrayList(1); + nameValuePairs.add(new BasicNameValuePair(FavoritesActivity.this.getString(R.string.parameter_songid), Integer.toString(song.getId()))); + post.setEntity(new UrlEncodedFormEntity(nameValuePairs)); + + HttpResponse response = client.execute(post); + BufferedReader rd = new BufferedReader(new InputStreamReader(response.getEntity().getContent())); + rd.close(); // I don't need to read the output + + FetchUrl.Result data = FetchUrl.get(new URL(baseUrl+FavoritesActivity.this.getString(R.string.url_song)+ song.getId())); + String songinfo = data.getResponse(); + if (songinfo.contains("True")) { + song.mLocked = true; + + v.setEnabled(false); + Toast.makeText(FavoritesActivity.this, "Requesting song: " + song.getTitle(), Toast.LENGTH_LONG).show(); + } else { + Toast.makeText(FavoritesActivity.this, "Song already locked or too much songs queued :(", Toast.LENGTH_LONG).show(); + } + } catch (final Exception e) { + Toast.makeText(FavoritesActivity.this, "Requesting song failed with error=" + e, Toast.LENGTH_LONG).show(); + } + } + + } + + + /** Initialize the contents of the Activity's standard options menu. */ + @Override + public boolean onCreateOptionsMenu(Menu menu) { + MenuInflater inflater = getMenuInflater(); + inflater.inflate(R.menu.main_options, menu); + return true; + } + + + /** Called whenever an item in the Activity's options menu is selected. */ + @Override + public boolean onOptionsItemSelected(MenuItem item) + { + switch(item.getItemId()) { + case R.id.settings_item: + // Show the settings activity. + Intent settingsActivity = new Intent(this, SettingsActivity.class); + startActivity(settingsActivity); + return true; + + case R.id.refresh_item: + // Refresh the playlist. + mFavoritesManager.cancelUpdate(); + mFavoritesManager.update(this, false); + return true; + + default: + return false; + } + } + + + // Override in API level 5+ + public void onBackPressed() + { + // Use a different transition depending on which playlist we're showing. + finish(); + Transition.set(this, R.anim.slide_in_left, R.anim.slide_out_right); + } + + + /// + /// Favorites event handlers + /// + + public void onTaskStarted(Object manager) + { + setProgressBarIndeterminateVisibility(true); + } + + public void onTaskFinished(Object manager, Object result) + { + //System.out.println("RESULT="+result); + Favorites favorites = (Favorites)result; + mFavorites = favorites; + mListAdapter.setFavorites(mFavorites); + setProgressBarIndeterminateVisibility(false); + } + + public void onTaskCancelled(Object manager) + { + setProgressBarIndeterminateVisibility(false); + } + + public void onTaskFailed(Object manager) + { + setProgressBarIndeterminateVisibility(false); + Toast errorToast = Toast.makeText(this, R.string.favorites_failed, Toast.LENGTH_SHORT); + errorToast.show(); + } +} diff --git a/app/src/main/java/com/kvance/Nectroid/FavoritesAdapter.java b/app/src/main/java/com/kvance/Nectroid/FavoritesAdapter.java new file mode 100644 index 0000000..5207834 --- /dev/null +++ b/app/src/main/java/com/kvance/Nectroid/FavoritesAdapter.java @@ -0,0 +1,112 @@ +// This file is part of Nectroid. +// +// Nectroid 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. +// +// Nectroid 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 Nectroid. If not, see . + +package com.kvance.Nectroid; + +import android.content.Context; +import android.view.View; +import android.view.ViewGroup; +import android.widget.BaseAdapter; +import android.widget.TextView; + + +class FavoritesAdapter extends BaseAdapter +{ + protected Favorites mFavorites; + protected Context mContext; + + public FavoritesAdapter(Favorites favorites, Context context) + { + super(); + mFavorites = favorites; + mContext = context; + } + + /// + /// Adapter methods + /// + + @Override + public int getCount() + { + if(mFavorites == null) { + return 0; + } else { + return mFavorites.getFavorites().size(); + } + } + + + @Override + public Object getItem(int position) + { + return mFavorites.getFavorites().get(position); + } + + /** Set how many songs into the playlist we currently are. + * + * If mPlaylist.getCurrentEntry() is playling, offset = 0 + * If mPlaylist.getQueue().get(0) is playling, offset = 1 + * and so on. + */ + public void setQueueOffset(int newOffset) + { + notifyDataSetChanged(); + } + + + public void setFavorites(Favorites favorites) + { + mFavorites = favorites; + notifyDataSetChanged(); + } + + + /// + /// Adapter methods + /// + + @Override + public long getItemId(int position) + { + Favorites.Entry entry = (Favorites.Entry)getItem(position); + return entry.getId(); + } + + + @Override + public boolean hasStableIds() + { + return true; + } + + + @Override + public View getView(int position, View convertView, ViewGroup parent) + { + TextView view; + if(convertView == null) { + view = new TextView(mContext); + } else { + view = (TextView)convertView; + } + + Favorites.Entry entry = (Favorites.Entry)getItem(position); + view.setText(entry.getTitle()); + view.setEnabled(!entry.isLocked()); + + return view; + } +} diff --git a/app/src/main/java/com/kvance/Nectroid/FavoritesManager.java b/app/src/main/java/com/kvance/Nectroid/FavoritesManager.java new file mode 100644 index 0000000..00c6437 --- /dev/null +++ b/app/src/main/java/com/kvance/Nectroid/FavoritesManager.java @@ -0,0 +1,139 @@ +// This file is part of Nectroid. +// +// Nectroid 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. +// +// Nectroid 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 Nectroid. If not, see . + +package com.kvance.Nectroid; + +import android.content.Context; + +import org.xml.sax.SAXException; + +import java.util.Date; +import java.util.HashSet; + + +/** Manager responsible for updating a Favorites. + * + * Register for Favorites update events with addTaskListener(). + * Register for song update events with addSongListener(). + * + * If your activity or service wants the Favorites to be automatically refreshed before it gets + * stale, call requestAutoUpdate(). Once you no longer need them, make sure to unregister your + * request with unrequestAutoUpdate(). + */ +public class FavoritesManager extends AutoRefreshDocManager +{ + private Favorites mFavorites; + + // Milliseconds before we're considered "almost done" + private static final long ALMOST_DONE_TIME = 25000; + + // Minimum time between auto-refreshes (in ms) + private static final long MIN_AUTO_REFRESH_TIME = 30000; + + private static final String TAG = "Nectroid"; + + + public FavoritesManager(Context applicationContext) + { + super(applicationContext); + } + + /** Return the QUEUE DocId. */ + @Override + public Cache.DocId getDocId() + { + return Cache.DocId.FAVORITES; + } + + /** Parse an XML file into a Favorites object. */ + @Override + public Favorites parseDocument(String xmlData, Context context) + { + //System.out.println("PARSE DOCUMENT="+xmlData); + try { + mFavorites = new Favorites(xmlData); + //System.out.println("nb of favs: "+mFavorites.getFavorites().size()); + return mFavorites; + } catch(SAXException e) { + return null; + } + } + + + @Override + public void onParserSuccess(Favorites result, Context context) + { + super.onParserSuccess(result, context); + } + + + /// + /// Public interface + /// + + public void onLowMemory() + { + // We can release the Favorites if nothing's using it. + if(mUpdateTask == null) { + mFavorites = null; + } + } + + + /** Reset all data. */ + public void reset() { + cancelUpdate(); + } + + + /// + /// Getters + /// + + public Favorites getFavorites() { return mFavorites; } + + /// + /// Auto-refresh methods + /// + + @Override + protected void scheduleNextRefresh(Context context) + { + long delay = 1000L*60L; + + // Remove any old auto-update callbacks before posting this one. + mHandler.removeCallbacks(autoUpdateFavorites); + mHandler.postDelayed(autoUpdateFavorites, delay); + } + + @Override + protected void unscheduleNextRefresh(Context context) + { + mHandler.removeCallbacks(autoUpdateFavorites); + } + + private Runnable autoUpdateFavorites = new Runnable() { + public void run() { + update(mContext, false); + // The next update will be scheduled after our UpdateTask completes. + } + }; + + @Override + protected boolean hasDocument() + { + return (mFavorites != null); + } +} diff --git a/src/com/kvance/Nectroid/FetchUrl.java b/app/src/main/java/com/kvance/Nectroid/FetchUrl.java similarity index 97% rename from src/com/kvance/Nectroid/FetchUrl.java rename to app/src/main/java/com/kvance/Nectroid/FetchUrl.java index c6e6d3d..c49caa6 100644 --- a/src/com/kvance/Nectroid/FetchUrl.java +++ b/app/src/main/java/com/kvance/Nectroid/FetchUrl.java @@ -37,6 +37,7 @@ public static class Result public static Result get(URL url) { + System.out.println("FETCHING: "+url); Result result = new Result(); StringBuilder sb = new StringBuilder(); boolean failed; diff --git a/src/com/kvance/Nectroid/ForegroundService.java b/app/src/main/java/com/kvance/Nectroid/ForegroundService.java similarity index 99% rename from src/com/kvance/Nectroid/ForegroundService.java rename to app/src/main/java/com/kvance/Nectroid/ForegroundService.java index ef4cc30..f9ee505 100644 --- a/src/com/kvance/Nectroid/ForegroundService.java +++ b/app/src/main/java/com/kvance/Nectroid/ForegroundService.java @@ -82,7 +82,8 @@ void startForegroundCompat(int id, Notification notification) { } // Fall back on the old API. - setForeground(true); + //setForeground(true); + mNM.notify(id, notification); } @@ -109,7 +110,7 @@ void stopForegroundCompat(int id) { // Fall back on the old API. Note to cancel BEFORE changing the // foreground state, since we could be killed at that point. mNM.cancel(id); - setForeground(false); + //setForeground(false); } @Override diff --git a/src/com/kvance/Nectroid/MP3Streamer.java b/app/src/main/java/com/kvance/Nectroid/MP3Streamer.java similarity index 100% rename from src/com/kvance/Nectroid/MP3Streamer.java rename to app/src/main/java/com/kvance/Nectroid/MP3Streamer.java diff --git a/src/com/kvance/Nectroid/NectroidActivity.java b/app/src/main/java/com/kvance/Nectroid/NectroidActivity.java similarity index 51% rename from src/com/kvance/Nectroid/NectroidActivity.java rename to app/src/main/java/com/kvance/Nectroid/NectroidActivity.java index 3795d93..1a4313a 100644 --- a/src/com/kvance/Nectroid/NectroidActivity.java +++ b/app/src/main/java/com/kvance/Nectroid/NectroidActivity.java @@ -15,13 +15,24 @@ package com.kvance.Nectroid; +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.URL; +import java.net.URLConnection; +import java.util.ArrayList; import java.util.List; import android.app.Activity; import android.content.Context; import android.content.Intent; +import android.content.SharedPreferences; import android.database.sqlite.SQLiteDatabase; import android.net.Uri; import android.os.Bundle; @@ -31,21 +42,43 @@ import android.text.method.LinkMovementMethod; import android.text.method.MovementMethod; import android.util.Log; +import android.view.KeyEvent; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View.OnClickListener; import android.view.View; import android.view.Window; +import android.widget.EditText; import android.widget.ImageButton; +import android.widget.LinearLayout; import android.widget.ListView; import android.widget.TextView; import android.widget.Toast; +import org.apache.http.HttpEntity; +import org.apache.http.HttpResponse; +import org.apache.http.NameValuePair; +import org.apache.http.client.HttpClient; +import org.apache.http.client.entity.UrlEncodedFormEntity; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.conn.scheme.Scheme; +import org.apache.http.conn.scheme.SchemeRegistry; +import org.apache.http.conn.ssl.SSLSocketFactory; +import org.apache.http.conn.ssl.X509HostnameVerifier; +import org.apache.http.impl.client.DefaultHttpClient; +import org.apache.http.impl.conn.SingleClientConnManager; +import org.apache.http.message.BasicNameValuePair; +import org.apache.http.params.BasicHttpParams; +import org.apache.http.params.HttpParams; +import org.apache.http.util.EntityUtils; + +import javax.net.ssl.HostnameVerifier; + public class NectroidActivity extends Activity - implements BackgroundTaskListener, PlaylistManager.SongListener, PlayerManager.StateListener -{ + implements BackgroundTaskListener, PlaylistManager.SongListener, PlayerManager.StateListener { // Widgets private TextView mCurrentlyPlayingView; private TextView mRequestedByView; @@ -63,9 +96,20 @@ public class NectroidActivity extends Activity private boolean mPaused; private Handler mHandler; private int mTimeLeft; + private TextView loginButton; + private LinearLayout whatyousayLayout; + private TextView historyButton; + private TextView favoritesButton; + private TextView queueButton; + private TextView whatyousayButton; + private EditText whatyousayEditText; // When a new stream is selected, store the choice here until we verify it works. - private class StreamChoice { URL stream; int id; } + private class StreamChoice { + URL stream; + int id; + } + private StreamChoice mStreamChoice; @@ -73,22 +117,23 @@ private class StreamChoice { URL stream; int id; } private static final int PICK_STREAM_REQUEST = 0; private static final String TAG = "Nectroid"; - + /// /// Activity event handlers /// - /** Called when the Activity is first created. */ + /** + * Called when the Activity is first created. + */ @Override - public void onCreate(Bundle savedInstanceState) - { + public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS); setContentView(R.layout.main); mHandler = new Handler(); - + Window window = getWindow(); getNectroidApp().updateWindowBackground(window); @@ -100,10 +145,11 @@ public void onCreate(Bundle savedInstanceState) } - /** Called when the Activity is being displayed to the user. */ + /** + * Called when the Activity is being displayed to the user. + */ @Override - protected void onStart() - { + protected void onStart() { super.onStart(); NectroidApplication app = getNectroidApp(); @@ -129,7 +175,7 @@ protected void onStart() // Update the GUI with the current song. Playlist.EntryAndTimeLeft currentSong = mPlaylistManager.getCurrentSong(); - if(currentSong != null) { + if (currentSong != null) { updatePlaylistUI(currentSong); } else { clearPlaylistUI(); @@ -137,7 +183,7 @@ protected void onStart() // Update the GUI with the current oneliners. OneLiner.List oneLiners = mOneLinerManager.getOneLiners(); - if(oneLiners != null) { + if (oneLiners != null) { mOneLinerAdapter.setOneLiners(oneLiners); } else { clearOneLiners(); @@ -145,10 +191,11 @@ protected void onStart() } - /** Called when the Activity is no longer visible to the user. */ + /** + * Called when the Activity is no longer visible to the user. + */ @Override - protected void onStop() - { + protected void onStop() { // Unsubscribe from various events. mPlaylistManager.removeTaskListener(this); mPlaylistManager.removeSongListener(this); @@ -159,10 +206,11 @@ protected void onStop() } - /** Called when the Activity is going into the background. */ + /** + * Called when the Activity is going into the background. + */ @Override - protected void onPause() - { + protected void onPause() { super.onPause(); mPaused = true; @@ -172,10 +220,11 @@ protected void onPause() } - /** Called when the Activity is ready to start interacting with the user. */ + /** + * Called when the Activity is ready to start interacting with the user. + */ @Override - protected void onResume() - { + protected void onResume() { super.onResume(); mPaused = false; @@ -188,21 +237,23 @@ protected void onResume() } - /** Initialize the contents of the Activity's standard options menu. */ + /** + * Initialize the contents of the Activity's standard options menu. + */ @Override - public boolean onCreateOptionsMenu(Menu menu) - { + public boolean onCreateOptionsMenu(Menu menu) { MenuInflater inflater = getMenuInflater(); inflater.inflate(R.menu.main_options, menu); return true; } - /** Called whenever an item in the Activity's options menu is selected. */ + /** + * Called whenever an item in the Activity's options menu is selected. + */ @Override - public boolean onOptionsItemSelected(MenuItem item) - { - switch(item.getItemId()) { + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { case R.id.settings_item: // Show the settings activity. Intent settingsActivity = new Intent(this, SettingsActivity.class); @@ -229,13 +280,14 @@ public boolean onOptionsItemSelected(MenuItem item) } - /** Called when an Activity that this Activity launched exits. */ + /** + * Called when an Activity that this Activity launched exits. + */ @Override - public void onActivityResult(int requestCode, int resultCode, Intent data) - { - switch(requestCode) { + public void onActivityResult(int requestCode, int resultCode, Intent data) { + switch (requestCode) { case PICK_STREAM_REQUEST: - if(resultCode == Activity.RESULT_OK) { + if (resultCode == Activity.RESULT_OK) { int id = data.getIntExtra(StreamsActivity.EXTRA_ID, -1); int bitrate = data.getIntExtra(StreamsActivity.EXTRA_BITRATE, 192); onStreamPicked(data.getData(), id, bitrate); @@ -254,21 +306,19 @@ public void onActivityResult(int requestCode, int resultCode, Intent data) /// @Override - public void onTaskStarted(Object manager) - { + public void onTaskStarted(Object manager) { setProgressBarIndeterminateVisibility(true); } @Override - public void onTaskFinished(Object manager, Object result) - { + public void onTaskFinished(Object manager, Object result) { // Determine what task was finished, and call the appropriate event handler. Class managerClass = manager.getClass(); - if(managerClass == PlaylistManager.class) { + if (managerClass == PlaylistManager.class) { mHandler.post(onPlaylistUpdateTaskFinished); - } else if(managerClass == OneLinerManager.class) { - OneLiner.List oneLiners = (OneLiner.List)result; + } else if (managerClass == OneLinerManager.class) { + OneLiner.List oneLiners = (OneLiner.List) result; mHandler.post(new OnOneLinerUpdateTaskFinished(oneLiners)); } else { @@ -277,8 +327,7 @@ public void onTaskFinished(Object manager, Object result) } @Override - public void onTaskCancelled(Object manager) - { + public void onTaskCancelled(Object manager) { mHandler.post(new Runnable() { public void run() { setProgressBarIndeterminateVisibility(getNectroidApp().isLoadingAnything()); @@ -287,13 +336,14 @@ public void run() { } @Override - public void onTaskFailed(Object manager) - { + public void onTaskFailed(Object manager) { int stringId; Class managerClass = manager.getClass(); - if(managerClass == PlaylistManager.class) { + if (managerClass == PlaylistManager.class) { stringId = R.string.playlist_failed; - } else if(managerClass == OneLinerManager.class) { + } else if (managerClass == FavoritesManager.class) { + stringId = R.string.favorites_failed; + } else if (managerClass == OneLinerManager.class) { stringId = R.string.oneliner_failed; } else { throw new RuntimeException(String.format("Unknown task failed %s", managerClass)); @@ -335,8 +385,7 @@ public void run() { /// @Override - public void onSongChanged(Playlist.EntryAndTimeLeft newSong) - { + public void onSongChanged(Playlist.EntryAndTimeLeft newSong) { updatePlaylistUI(newSong); } @@ -345,17 +394,18 @@ public void onSongChanged(Playlist.EntryAndTimeLeft newSong) /// UI event handlers /// - /** The play button was clicked */ + /** + * The play button was clicked + */ private OnClickListener onPlayButtonClicked = new OnClickListener() { - public void onClick(View v) - { + public void onClick(View v) { // The play button can mean "play" or "stop" depending on the player state. - if(mPlayerManager.getPlayerState() != PlayerService.State.STOPPED) { + if (mPlayerManager.getPlayerState() != PlayerService.State.STOPPED) { stopStream(); } else { // Check if we have a URL to play. URL streamUrl = Prefs.getStreamUrl(NectroidActivity.this); - if(streamUrl != null) { + if (streamUrl != null) { int bitrate = getBitrateForStreamUrl(streamUrl); Log.d(TAG, String.format("Play stream \"%s\" (%d kbps)", streamUrl, bitrate)); playStream(streamUrl, bitrate); @@ -370,10 +420,11 @@ public void onClick(View v) }; - /** The "history" link was clicked */ + /** + * The "history" link was clicked + */ private OnClickListener onHistoryClicked = new OnClickListener() { - public void onClick(View widget) - { + public void onClick(View widget) { Activity us = NectroidActivity.this; Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse("nectroid://history")); intent.setClass(us, PlaylistActivity.class); @@ -383,10 +434,11 @@ public void onClick(View widget) }; - /** The "queue" link was clicked */ + /** + * The "queue" link was clicked + */ private OnClickListener onQueueClicked = new OnClickListener() { - public void onClick(View widget) - { + public void onClick(View widget) { Activity us = NectroidActivity.this; Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse("nectroid://queue")); intent.setClass(us, PlaylistActivity.class); @@ -395,35 +447,217 @@ public void onClick(View widget) } }; + /** + * The "favorites" link was clicked + */ + private OnClickListener onFavoritesClicked = new OnClickListener() { + public void onClick(View widget) { + + Activity us = NectroidActivity.this; + Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse("nectroid://favorites")); + intent.setClass(us, FavoritesActivity.class); + startActivity(intent); + Transition.set(us, R.anim.slide_in_right, R.anim.slide_out_left); + + } + }; + + /** + * The "favorites" link was clicked + */ + private OnClickListener onWhatYouSayClicked = new OnClickListener() { + public void onClick(View widget) { + String text = whatyousayEditText.getText().toString(); + if (text.trim().length() == 0) return; + + try { +// Find the base URL for the current site. + NectroidApplication app = (NectroidApplication)getApplication(); + String baseUrl = app.getSiteManager().getCurrentSite().getBaseUrl(); + + HttpClient client = app.getHttpClient(); + + HttpPost post = new HttpPost(baseUrl+NectroidActivity.this.getString(R.string.url_whatyousay)); + System.out.println("WHATYOUSAY URL="+baseUrl+NectroidActivity.this.getString(R.string.url_whatyousay)); + List nameValuePairs = new ArrayList(1); + nameValuePairs.add(new BasicNameValuePair(NectroidActivity.this.getString(R.string.parameter_line), text)); + post.setEntity(new UrlEncodedFormEntity(nameValuePairs)); + + HttpResponse response = client.execute(post); + BufferedReader rd = new BufferedReader(new InputStreamReader(response.getEntity().getContent())); + String line = ""; + while ((line = rd.readLine()) != null) { + //System.out.println(line); + } + + Toast.makeText(NectroidActivity.this, "whatyousay=" + text, Toast.LENGTH_LONG).show(); + whatyousayEditText.getText().clear(); + mOneLinerManager.update(NectroidActivity.this, false); + } catch (Exception e) { + Toast.makeText(NectroidActivity.this, "Could not whatyousay error=" + e, Toast.LENGTH_LONG).show(); + } + } + }; - /** One second has elapsed */ + private static String readStream(InputStream in) { + BufferedReader reader = null; + StringBuffer response = new StringBuffer(); + try { + reader = new BufferedReader(new InputStreamReader(in)); + String line = ""; + while ((line = reader.readLine()) != null) { + response.append(line); + } + } catch (IOException e) { + e.printStackTrace(); + } finally { + if (reader != null) { + try { + reader.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + return response.toString(); + } + + public static boolean findLineInStream(BufferedReader rd, String tofind, String stopIfFound) throws IOException { + + String line = ""; + while ((line = rd.readLine()) != null) { + if (tofind != null && line.trim().contains(tofind)) { + return true; + } else if (stopIfFound != null && line.trim().contains(stopIfFound)) { + return false; + } + } + rd.close(); + + return false; + } + + /** + * The "login" link was clicked + */ + private OnClickListener onLogInClicked = new OnClickListener() { + public void onClick(View widget) { + + String login = Prefs.getLogin(NectroidActivity.this); + String password = Prefs.getPassword(NectroidActivity.this); + + if (login == null || password == null) { + Toast.makeText(NectroidActivity.this, "Cannot login: your login is not set in preferences.", Toast.LENGTH_LONG).show(); + Intent settingsActivity = new Intent(NectroidActivity.this, SettingsActivity.class); + startActivity(settingsActivity); + return; + } + if (password == null) { + Toast.makeText(NectroidActivity.this, "Cannot login: your password is not set in preferences.", Toast.LENGTH_LONG).show(); + Intent settingsActivity = new Intent(NectroidActivity.this, SettingsActivity.class); + startActivity(settingsActivity); + return; + } + + NectroidApplication app = (NectroidApplication)getApplication(); + String baseUrl = app.getSiteManager().getCurrentSite().getBaseUrl(); + + if (whatyousayLayout.getVisibility() == LinearLayout.VISIBLE) { + // DefaultHttpClient client = null; + try { + + HttpPost post = new HttpPost(baseUrl+NectroidActivity.this.getString(R.string.url_logout)); + System.out.println("LOGOUT URL="+baseUrl+NectroidActivity.this.getString(R.string.url_logout)); + List nameValuePairs = new ArrayList(); + post.setEntity(new UrlEncodedFormEntity(nameValuePairs)); + + HttpClient client = app.getHttpClient(); + HttpResponse response = client.execute(post); + + BufferedReader rd = new BufferedReader(new InputStreamReader(response.getEntity().getContent())); + + if (findLineInStream(rd, "Welcome, " + login, "Welcome, Visitor!")) { + Toast.makeText(NectroidActivity.this, "Could not log out with login=" + login, Toast.LENGTH_LONG).show(); + } else { + Toast.makeText(NectroidActivity.this, "Logged out", Toast.LENGTH_LONG).show(); + whatyousayLayout.setVisibility(LinearLayout.INVISIBLE); + favoritesButton.setVisibility(LinearLayout.INVISIBLE); + loginButton.setText("Log in"); + + Cache.setUser(null); + } + } catch (Exception e) { + Toast.makeText(NectroidActivity.this, "Cannot login: with error " + e, Toast.LENGTH_LONG).show(); + return; + } finally { + + } + } else { + + // DefaultHttpClient client = null; + try { + HttpPost post = new HttpPost(baseUrl+NectroidActivity.this.getString(R.string.url_login)); + System.out.println("LOGIN_KEY URL="+baseUrl+NectroidActivity.this.getString(R.string.url_login)); + List nameValuePairs = new ArrayList(1); + nameValuePairs.add(new BasicNameValuePair(NectroidActivity.this.getString(R.string.parameter_username), login)); + nameValuePairs.add(new BasicNameValuePair(NectroidActivity.this.getString(R.string.parameter_password), password)); + post.setEntity(new UrlEncodedFormEntity(nameValuePairs)); + + HttpClient client = app.getHttpClient(); + HttpResponse response = client.execute(post); + BufferedReader rd = new BufferedReader(new InputStreamReader(response.getEntity().getContent())); + + if (findLineInStream(rd, "Welcome, " + login, "Welcome, Visitor!")) { + Toast.makeText(NectroidActivity.this, "Logged as " + login, Toast.LENGTH_LONG).show(); + whatyousayLayout.setVisibility(LinearLayout.VISIBLE); + favoritesButton.setVisibility(LinearLayout.VISIBLE); + loginButton.setText("Log out"); + Cache.setUser(login); + } else { + Toast.makeText(NectroidActivity.this, "Could not log (still visitor) in with login=" + login, Toast.LENGTH_LONG).show(); + } + } catch (Exception e) { + Toast.makeText(NectroidActivity.this, "Cannot login: with error " + e, Toast.LENGTH_LONG).show(); + return; + } finally { + + } + } + + } + }; + + /** + * One second has elapsed + */ final Runnable onTick = new Runnable() { public void run() { // Update the 1-second timer. - if(mTimeLeft > 0) { + if (mTimeLeft > 0) { mTimeLeft--; updateTimeLeft(); } // Schedule the next tick. - if(!mPaused) { + if (!mPaused) { mHandler.postDelayed(this, 1000); } } }; - /** The user selected a stream to play. */ - private void onStreamPicked(Uri streamUri, int id, int bitrate) - { + /** + * The user selected a stream to play. + */ + private void onStreamPicked(Uri streamUri, int id, int bitrate) { // Activities return URIs, but we need a URL. URL streamUrl = null; try { streamUrl = new URL(streamUri.toString()); - } catch(MalformedURLException e) { + } catch (MalformedURLException e) { Toast.makeText(this, R.string.invalid_stream, Toast.LENGTH_SHORT).show(); } - if(streamUrl != null) { + if (streamUrl != null) { // Remember the choice. Once we verify we can play the stream, save it to prefs. mStreamChoice = new StreamChoice(); mStreamChoice.stream = streamUrl; @@ -437,9 +671,8 @@ private void onStreamPicked(Uri streamUri, int id, int bitrate) /// Player event handlers /// - public void onStateChanged(PlayerService.State state) - { - switch(state) { + public void onStateChanged(PlayerService.State state) { + switch (state) { case STOPPED: mPlayButton.setImageResource(R.drawable.play); break; @@ -447,7 +680,7 @@ public void onStateChanged(PlayerService.State state) case PLAYING: // If we selected a new stream, we can now remember the choice since we verified // that it's playable. - if(mStreamChoice != null) { + if (mStreamChoice != null) { getNectroidApp().onUserPickedStream(mStreamChoice.stream, mStreamChoice.id); mStreamChoice = null; } @@ -465,18 +698,20 @@ public void onStateChanged(PlayerService.State state) /// Playlist commands /// - /** Update the playlist part of the UI with this song. */ - private void updatePlaylistUI(Playlist.EntryAndTimeLeft ent) - { + /** + * Update the playlist part of the UI with this song. + */ + private void updatePlaylistUI(Playlist.EntryAndTimeLeft ent) { mTimeLeft = ent.getTimeLeft(); updateTimeLeft(); updateCurrentlyPlaying(ent.getEntry()); updateRequestedBy(ent.getEntry()); } - /** Clear the playlist views. */ - private void clearPlaylistUI() - { + /** + * Clear the playlist views. + */ + private void clearPlaylistUI() { mTimeLeft = -1; mTimeLeftView.setText(""); mCurrentlyPlayingView.setText(""); @@ -488,15 +723,16 @@ private void clearPlaylistUI() /// Other commands /// - /** Create our widget references after the activity has been created. */ - private void createWidgets() - { + /** + * Create our widget references after the activity has been created. + */ + private void createWidgets() { // Get widget references - mCurrentlyPlayingView = (TextView)findViewById(R.id.currently_playing); - mRequestedByView = (TextView)findViewById(R.id.requested_by); - mTimeLeftView = (TextView)findViewById(R.id.time_left); - mOneLinerView = (ListView)findViewById(R.id.oneliner); - mPlayButton = (ImageButton)findViewById(R.id.play_button); + mCurrentlyPlayingView = (TextView) findViewById(R.id.currently_playing); + mRequestedByView = (TextView) findViewById(R.id.requested_by); + mTimeLeftView = (TextView) findViewById(R.id.time_left); + mOneLinerView = (ListView) findViewById(R.id.oneliner); + mPlayButton = (ImageButton) findViewById(R.id.play_button); // Make sure hypertext is clickable in textviews that have it. MovementMethod lmm = LinkMovementMethod.getInstance(); @@ -507,14 +743,47 @@ private void createWidgets() mPlayButton.setOnClickListener(onPlayButtonClicked); // Set up hypertext "buttons". - TextView historyButton = (TextView)findViewById(R.id.history_button); + historyButton = (TextView) findViewById(R.id.history_button); historyButton.setMovementMethod(lmm); historyButton.setOnClickListener(onHistoryClicked); - TextView queueButton = (TextView)findViewById(R.id.queue_button); + queueButton = (TextView) findViewById(R.id.queue_button); queueButton.setMovementMethod(lmm); queueButton.setOnClickListener(onQueueClicked); + //hide WHATYOUSAY LAYOUT + whatyousayLayout = (LinearLayout) findViewById(R.id.whatyousay_layout); + whatyousayLayout.setVisibility(LinearLayout.INVISIBLE); + + whatyousayButton = (TextView) findViewById(R.id.whatyousay_button); + whatyousayButton.setMovementMethod(lmm); + whatyousayButton.setOnClickListener(onWhatYouSayClicked); + + whatyousayEditText = (EditText) findViewById(R.id.whatyousay_edittext); + whatyousayEditText.setOnKeyListener(new View.OnKeyListener(){ + + @Override + public boolean onKey(View v, int keyCode, KeyEvent event) { + if ((event.getAction() == KeyEvent.ACTION_DOWN) && + (keyCode == KeyEvent.KEYCODE_ENTER)) { + + onWhatYouSayClicked.onClick(whatyousayButton); + + return true; + + } + return false; + } + }); + favoritesButton = (TextView) findViewById(R.id.favorites_button); + favoritesButton.setMovementMethod(lmm); + favoritesButton.setOnClickListener(onFavoritesClicked); + favoritesButton.setVisibility(LinearLayout.INVISIBLE); + + loginButton = (TextView) findViewById(R.id.login_button); + loginButton.setMovementMethod(lmm); + loginButton.setOnClickListener(onLogInClicked); + // Set up oneliner view. mOneLinerAdapter = new OneLinerAdapter(null, this); mOneLinerView.setAdapter(mOneLinerAdapter); @@ -522,18 +791,20 @@ private void createWidgets() } - /** Start the one-second timer. */ - private void startTimer() - { + /** + * Start the one-second timer. + */ + private void startTimer() { // Remove any existing timer first, just in case. mHandler.removeCallbacks(onTick); mHandler.postDelayed(onTick, 1000); } - /** Start the player service. */ - private void playStream(URL url, int bitrate) - { + /** + * Start the player service. + */ + private void playStream(URL url, int bitrate) { Intent intent = new Intent(this, PlayerService.class); intent.setAction(PlayerService.ACTION_PLAY); intent.setData(Uri.parse(url.toString())); @@ -541,18 +812,20 @@ private void playStream(URL url, int bitrate) startService(intent); } - /** Stop the player service. */ - private void stopStream() - { + /** + * Stop the player service. + */ + private void stopStream() { Intent intent = new Intent(this, PlayerService.class); boolean stopped = stopService(intent); Log.d(TAG, "stopService() returned " + String.valueOf(stopped)); } - /** Clear the OneLiners view. */ - private void clearOneLiners() - { + /** + * Clear the OneLiners view. + */ + private void clearOneLiners() { mOneLinerAdapter.setOneLiners(null); } @@ -560,8 +833,7 @@ private void clearOneLiners() /// Utilities /// - private void updateCurrentlyPlaying(Playlist.Entry entry) - { + private void updateCurrentlyPlaying(Playlist.Entry entry) { // Build the formatted string StringBuilder html = new StringBuilder(); String baseUrl = getNectroidApp().getSiteManager().getCurrentSite().getBaseUrl(); @@ -569,11 +841,11 @@ private void updateCurrentlyPlaying(Playlist.Entry entry) html.append(String.format("%s by ", songLink, entry.getTitle())); java.util.List artists = entry.getArtists(); int numArtists = artists.size(); - for(int i = 0; i < numArtists; i++) { + for (int i = 0; i < numArtists; i++) { Playlist.IdString artist = artists.get(i); String link = baseUrl + entry.artistLink(artist, this); html.append(String.format("%s", link, artist.getString())); - if(i < (numArtists - 1)) { + if (i < (numArtists - 1)) { html.append(", "); } } @@ -584,8 +856,7 @@ private void updateCurrentlyPlaying(Playlist.Entry entry) } - private void updateRequestedBy(Playlist.Entry entry) - { + private void updateRequestedBy(Playlist.Entry entry) { // Build the formatted string String baseUrl = getNectroidApp().getSiteManager().getCurrentSite().getBaseUrl(); String requesterLink = baseUrl + entry.requesterLink(this); @@ -598,23 +869,20 @@ private void updateRequestedBy(Playlist.Entry entry) } - private void updateTimeLeft() - { + private void updateTimeLeft() { int time = Math.max(0, mTimeLeft); mTimeLeftView.setText(String.format("%d:%02d", time / 60, time % 60)); } - private void updateTitle() - { + private void updateTitle() { String siteName = getNectroidApp().getSiteManager().getCurrentSite().getName(); String appName = this.getString(R.string.app_name); setTitle(appName + " - " + siteName); } - private int getBitrateForStreamUrl(URL streamUrl) - { + private int getBitrateForStreamUrl(URL streamUrl) { String urlString = streamUrl.toString(); int bitrate = 192; @@ -627,29 +895,28 @@ private int getBitrateForStreamUrl(URL streamUrl) db.close(); } - if(streams == null) { + if (streams == null) { Log.w(TAG, "Couldn't open streams database; using unknown bitrate"); } else { // Search the stream list for that URL. boolean found = false; - for(Stream stream : streams) { - if(stream.getUrl().toString().equals(urlString)) { + for (Stream stream : streams) { + if (stream.getUrl().toString().equals(urlString)) { bitrate = stream.getBitrate(); found = true; break; } } - if(!found) { + if (!found) { Log.w(TAG, String.format("Couldn't find bitrate for stream \"%s\"", urlString)); } } return bitrate; } - - private NectroidApplication getNectroidApp() - { - return (NectroidApplication)getApplication(); + + private NectroidApplication getNectroidApp() { + return (NectroidApplication) getApplication(); } } diff --git a/src/com/kvance/Nectroid/NectroidApplication.java b/app/src/main/java/com/kvance/Nectroid/NectroidApplication.java similarity index 87% rename from src/com/kvance/Nectroid/NectroidApplication.java rename to app/src/main/java/com/kvance/Nectroid/NectroidApplication.java index ab7c0ea..3e18715 100644 --- a/src/com/kvance/Nectroid/NectroidApplication.java +++ b/app/src/main/java/com/kvance/Nectroid/NectroidApplication.java @@ -28,6 +28,12 @@ import android.util.Log; import android.view.Window; +import org.apache.http.client.HttpClient; +import org.apache.http.conn.ClientConnectionManager; +import org.apache.http.impl.client.DefaultHttpClient; +import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager; +import org.apache.http.params.HttpParams; + public class NectroidApplication extends Application implements OnSharedPreferenceChangeListener, SiteManager.SiteListener @@ -35,6 +41,7 @@ public class NectroidApplication extends Application private OneLinerManager mOneLinerManager; private PlayerManager mPlayerManager; private PlaylistManager mPlaylistManager; + private FavoritesManager mFavoritesManager; private Scrobbler mScrobbler; private SiteManager mSiteManager; private StreamsManager mStreamsManager; @@ -43,6 +50,7 @@ public class NectroidApplication extends Application private static final String TAG = "Nectroid"; + HttpClient client = new DefaultHttpClient(); @Override public void onCreate() @@ -60,6 +68,7 @@ public void onCreate() Cache.setSite(mSiteManager.getCurrentSite(), appContext); mPlaylistManager = new PlaylistManager(appContext); + mFavoritesManager = new FavoritesManager(appContext); mStreamsManager = new StreamsManager(); mPlayerManager = new PlayerManager(); @@ -88,6 +97,7 @@ public void onCreate() public void onLowMemory() { mPlaylistManager.onLowMemory(); + mFavoritesManager.onLowMemory(); mStreamsManager.onLowMemory(); mOneLinerManager.onLowMemory(); } @@ -131,6 +141,7 @@ public void onUserPickedStream(URL streamUrl, int streamId) /// Getters /// + public FavoritesManager getFavoritesManager() { return mFavoritesManager; } public PlaylistManager getPlaylistManager() { return mPlaylistManager; } public StreamsManager getStreamsManager() { return mStreamsManager; } public PlayerManager getPlayerManager() { return mPlayerManager; } @@ -145,10 +156,11 @@ public void onUserPickedStream(URL streamUrl, int streamId) /** Return true if any of our managers are loading something. */ public boolean isLoadingAnything() { - return mPlaylistManager.isUpdating() || - mStreamsManager.isUpdating() || - mOneLinerManager.isUpdating() || - mPlayerManager.getPlayerState() == PlayerService.State.LOADING; + return mFavoritesManager.isUpdating() || + mPlaylistManager.isUpdating() || + mStreamsManager.isUpdating() || + mOneLinerManager.isUpdating() || + mPlayerManager.getPlayerState() == PlayerService.State.LOADING; } /** Update this window's background to the current background color. */ @@ -190,6 +202,20 @@ public void onSharedPreferenceChanged(SharedPreferences prefs, String key) } } + public HttpClient getHttpClient() { + if (client != null) return client; + + DefaultHttpClient client = new DefaultHttpClient(); + ClientConnectionManager mgr = client.getConnectionManager(); + HttpParams params = client.getParams(); + + client = new DefaultHttpClient(new ThreadSafeClientConnManager(params, + mgr.getSchemeRegistry()), params); + + return client; + + } + /// /// Site updates @@ -210,6 +236,7 @@ public void onSiteChanged(Site newSite) // Reset all document managers. mPlaylistManager.reset(); + mFavoritesManager.reset(); mOneLinerManager.reset(); mStreamsManager.reset(); Prefs.clearOneLinerUpdateTime(appContext); diff --git a/src/com/kvance/Nectroid/OneLiner.java b/app/src/main/java/com/kvance/Nectroid/OneLiner.java similarity index 100% rename from src/com/kvance/Nectroid/OneLiner.java rename to app/src/main/java/com/kvance/Nectroid/OneLiner.java diff --git a/src/com/kvance/Nectroid/OneLinerAdapter.java b/app/src/main/java/com/kvance/Nectroid/OneLinerAdapter.java similarity index 100% rename from src/com/kvance/Nectroid/OneLinerAdapter.java rename to app/src/main/java/com/kvance/Nectroid/OneLinerAdapter.java diff --git a/src/com/kvance/Nectroid/OneLinerManager.java b/app/src/main/java/com/kvance/Nectroid/OneLinerManager.java similarity index 100% rename from src/com/kvance/Nectroid/OneLinerManager.java rename to app/src/main/java/com/kvance/Nectroid/OneLinerManager.java diff --git a/src/com/kvance/Nectroid/PlayerManager.java b/app/src/main/java/com/kvance/Nectroid/PlayerManager.java similarity index 100% rename from src/com/kvance/Nectroid/PlayerManager.java rename to app/src/main/java/com/kvance/Nectroid/PlayerManager.java diff --git a/src/com/kvance/Nectroid/PlayerService.java b/app/src/main/java/com/kvance/Nectroid/PlayerService.java similarity index 100% rename from src/com/kvance/Nectroid/PlayerService.java rename to app/src/main/java/com/kvance/Nectroid/PlayerService.java diff --git a/src/com/kvance/Nectroid/Playlist.java b/app/src/main/java/com/kvance/Nectroid/Playlist.java similarity index 100% rename from src/com/kvance/Nectroid/Playlist.java rename to app/src/main/java/com/kvance/Nectroid/Playlist.java diff --git a/src/com/kvance/Nectroid/PlaylistActivity.java b/app/src/main/java/com/kvance/Nectroid/PlaylistActivity.java similarity index 100% rename from src/com/kvance/Nectroid/PlaylistActivity.java rename to app/src/main/java/com/kvance/Nectroid/PlaylistActivity.java diff --git a/src/com/kvance/Nectroid/PlaylistAdapter.java b/app/src/main/java/com/kvance/Nectroid/PlaylistAdapter.java similarity index 100% rename from src/com/kvance/Nectroid/PlaylistAdapter.java rename to app/src/main/java/com/kvance/Nectroid/PlaylistAdapter.java diff --git a/src/com/kvance/Nectroid/PlaylistHistoryAdapter.java b/app/src/main/java/com/kvance/Nectroid/PlaylistHistoryAdapter.java similarity index 100% rename from src/com/kvance/Nectroid/PlaylistHistoryAdapter.java rename to app/src/main/java/com/kvance/Nectroid/PlaylistHistoryAdapter.java diff --git a/src/com/kvance/Nectroid/PlaylistManager.java b/app/src/main/java/com/kvance/Nectroid/PlaylistManager.java similarity index 100% rename from src/com/kvance/Nectroid/PlaylistManager.java rename to app/src/main/java/com/kvance/Nectroid/PlaylistManager.java diff --git a/src/com/kvance/Nectroid/PlaylistQueueAdapter.java b/app/src/main/java/com/kvance/Nectroid/PlaylistQueueAdapter.java similarity index 100% rename from src/com/kvance/Nectroid/PlaylistQueueAdapter.java rename to app/src/main/java/com/kvance/Nectroid/PlaylistQueueAdapter.java diff --git a/src/com/kvance/Nectroid/Prefs.java b/app/src/main/java/com/kvance/Nectroid/Prefs.java similarity index 95% rename from src/com/kvance/Nectroid/Prefs.java rename to app/src/main/java/com/kvance/Nectroid/Prefs.java index 6cc5002..685ecd9 100644 --- a/src/com/kvance/Nectroid/Prefs.java +++ b/app/src/main/java/com/kvance/Nectroid/Prefs.java @@ -41,7 +41,8 @@ class Prefs public static final String SITE_ID_KEY = "site_id"; public static final String CACHED_SITE_ID_KEY = "cached_site_id"; public static final String USE_SW_DECODER_KEY = "use_sw_decoder"; - + public static final String LOGIN_KEY = "login"; + public static final String PASSWORD_KEY = "password"; // Timestamp formatter private static final SimpleDateFormat timestampFormat = new SimpleDateFormat( "yyyy-MM-dd HH:mm:ss"); @@ -99,6 +100,16 @@ public static boolean getUseSWDecoder(Context context) return sp(context).getBoolean(USE_SW_DECODER_KEY, DEFAULT_USE_SW_DECODER); } + public static String getLogin(Context context) + { + return sp(context).getString(LOGIN_KEY, null); + } + + public static String getPassword(Context context) + { + return sp(context).getString(PASSWORD_KEY, null); + } + public static URL getStreamUrl(Context context) { URL retval = null; diff --git a/src/com/kvance/Nectroid/RingBuffer.java b/app/src/main/java/com/kvance/Nectroid/RingBuffer.java similarity index 100% rename from src/com/kvance/Nectroid/RingBuffer.java rename to app/src/main/java/com/kvance/Nectroid/RingBuffer.java diff --git a/src/com/kvance/Nectroid/Scrobbler.java b/app/src/main/java/com/kvance/Nectroid/Scrobbler.java similarity index 100% rename from src/com/kvance/Nectroid/Scrobbler.java rename to app/src/main/java/com/kvance/Nectroid/Scrobbler.java diff --git a/src/com/kvance/Nectroid/SettingsActivity.java b/app/src/main/java/com/kvance/Nectroid/SettingsActivity.java similarity index 100% rename from src/com/kvance/Nectroid/SettingsActivity.java rename to app/src/main/java/com/kvance/Nectroid/SettingsActivity.java diff --git a/src/com/kvance/Nectroid/Site.java b/app/src/main/java/com/kvance/Nectroid/Site.java similarity index 100% rename from src/com/kvance/Nectroid/Site.java rename to app/src/main/java/com/kvance/Nectroid/Site.java diff --git a/src/com/kvance/Nectroid/SiteActivity.java b/app/src/main/java/com/kvance/Nectroid/SiteActivity.java similarity index 100% rename from src/com/kvance/Nectroid/SiteActivity.java rename to app/src/main/java/com/kvance/Nectroid/SiteActivity.java diff --git a/src/com/kvance/Nectroid/SiteManager.java b/app/src/main/java/com/kvance/Nectroid/SiteManager.java similarity index 100% rename from src/com/kvance/Nectroid/SiteManager.java rename to app/src/main/java/com/kvance/Nectroid/SiteManager.java diff --git a/src/com/kvance/Nectroid/SitePreference.java b/app/src/main/java/com/kvance/Nectroid/SitePreference.java similarity index 100% rename from src/com/kvance/Nectroid/SitePreference.java rename to app/src/main/java/com/kvance/Nectroid/SitePreference.java diff --git a/src/com/kvance/Nectroid/Stream.java b/app/src/main/java/com/kvance/Nectroid/Stream.java similarity index 100% rename from src/com/kvance/Nectroid/Stream.java rename to app/src/main/java/com/kvance/Nectroid/Stream.java diff --git a/src/com/kvance/Nectroid/StreamsActivity.java b/app/src/main/java/com/kvance/Nectroid/StreamsActivity.java similarity index 100% rename from src/com/kvance/Nectroid/StreamsActivity.java rename to app/src/main/java/com/kvance/Nectroid/StreamsActivity.java diff --git a/src/com/kvance/Nectroid/StreamsAdapter.java b/app/src/main/java/com/kvance/Nectroid/StreamsAdapter.java similarity index 100% rename from src/com/kvance/Nectroid/StreamsAdapter.java rename to app/src/main/java/com/kvance/Nectroid/StreamsAdapter.java diff --git a/src/com/kvance/Nectroid/StreamsManager.java b/app/src/main/java/com/kvance/Nectroid/StreamsManager.java similarity index 100% rename from src/com/kvance/Nectroid/StreamsManager.java rename to app/src/main/java/com/kvance/Nectroid/StreamsManager.java diff --git a/src/com/kvance/Nectroid/Transition.java b/app/src/main/java/com/kvance/Nectroid/Transition.java similarity index 100% rename from src/com/kvance/Nectroid/Transition.java rename to app/src/main/java/com/kvance/Nectroid/Transition.java diff --git a/res/anim/slide_in_left.xml b/app/src/main/res/anim/slide_in_left.xml similarity index 100% rename from res/anim/slide_in_left.xml rename to app/src/main/res/anim/slide_in_left.xml diff --git a/res/anim/slide_in_right.xml b/app/src/main/res/anim/slide_in_right.xml similarity index 100% rename from res/anim/slide_in_right.xml rename to app/src/main/res/anim/slide_in_right.xml diff --git a/res/anim/slide_out_left.xml b/app/src/main/res/anim/slide_out_left.xml similarity index 100% rename from res/anim/slide_out_left.xml rename to app/src/main/res/anim/slide_out_left.xml diff --git a/res/anim/slide_out_right.xml b/app/src/main/res/anim/slide_out_right.xml similarity index 100% rename from res/anim/slide_out_right.xml rename to app/src/main/res/anim/slide_out_right.xml diff --git a/res/drawable-hdpi/ic_launcher_nectroid.png b/app/src/main/res/drawable-hdpi/ic_launcher_nectroid.png similarity index 100% rename from res/drawable-hdpi/ic_launcher_nectroid.png rename to app/src/main/res/drawable-hdpi/ic_launcher_nectroid.png diff --git a/res/drawable-hdpi/ic_menu_refresh.png b/app/src/main/res/drawable-hdpi/ic_menu_refresh.png similarity index 100% rename from res/drawable-hdpi/ic_menu_refresh.png rename to app/src/main/res/drawable-hdpi/ic_menu_refresh.png diff --git a/res/drawable-hdpi/orange_waffle.png b/app/src/main/res/drawable-hdpi/orange_waffle.png similarity index 100% rename from res/drawable-hdpi/orange_waffle.png rename to app/src/main/res/drawable-hdpi/orange_waffle.png diff --git a/res/drawable-hdpi/play.png b/app/src/main/res/drawable-hdpi/play.png similarity index 100% rename from res/drawable-hdpi/play.png rename to app/src/main/res/drawable-hdpi/play.png diff --git a/res/drawable-hdpi/play_status.png b/app/src/main/res/drawable-hdpi/play_status.png similarity index 100% rename from res/drawable-hdpi/play_status.png rename to app/src/main/res/drawable-hdpi/play_status.png diff --git a/res/drawable-hdpi/stop.png b/app/src/main/res/drawable-hdpi/stop.png similarity index 100% rename from res/drawable-hdpi/stop.png rename to app/src/main/res/drawable-hdpi/stop.png diff --git a/res/drawable-ldpi/ic_launcher_nectroid.png b/app/src/main/res/drawable-ldpi/ic_launcher_nectroid.png similarity index 100% rename from res/drawable-ldpi/ic_launcher_nectroid.png rename to app/src/main/res/drawable-ldpi/ic_launcher_nectroid.png diff --git a/res/drawable-ldpi/ic_menu_refresh.png b/app/src/main/res/drawable-ldpi/ic_menu_refresh.png similarity index 100% rename from res/drawable-ldpi/ic_menu_refresh.png rename to app/src/main/res/drawable-ldpi/ic_menu_refresh.png diff --git a/res/drawable-ldpi/orange_waffle.png b/app/src/main/res/drawable-ldpi/orange_waffle.png similarity index 100% rename from res/drawable-ldpi/orange_waffle.png rename to app/src/main/res/drawable-ldpi/orange_waffle.png diff --git a/res/drawable-ldpi/play.png b/app/src/main/res/drawable-ldpi/play.png similarity index 100% rename from res/drawable-ldpi/play.png rename to app/src/main/res/drawable-ldpi/play.png diff --git a/res/drawable-ldpi/play_status.png b/app/src/main/res/drawable-ldpi/play_status.png similarity index 100% rename from res/drawable-ldpi/play_status.png rename to app/src/main/res/drawable-ldpi/play_status.png diff --git a/res/drawable-ldpi/stop.png b/app/src/main/res/drawable-ldpi/stop.png similarity index 100% rename from res/drawable-ldpi/stop.png rename to app/src/main/res/drawable-ldpi/stop.png diff --git a/res/drawable-mdpi/ic_launcher_nectroid.png b/app/src/main/res/drawable-mdpi/ic_launcher_nectroid.png similarity index 100% rename from res/drawable-mdpi/ic_launcher_nectroid.png rename to app/src/main/res/drawable-mdpi/ic_launcher_nectroid.png diff --git a/res/drawable-mdpi/ic_menu_refresh.png b/app/src/main/res/drawable-mdpi/ic_menu_refresh.png similarity index 100% rename from res/drawable-mdpi/ic_menu_refresh.png rename to app/src/main/res/drawable-mdpi/ic_menu_refresh.png diff --git a/res/drawable-mdpi/orange_waffle.png b/app/src/main/res/drawable-mdpi/orange_waffle.png similarity index 100% rename from res/drawable-mdpi/orange_waffle.png rename to app/src/main/res/drawable-mdpi/orange_waffle.png diff --git a/res/drawable-mdpi/play.png b/app/src/main/res/drawable-mdpi/play.png similarity index 100% rename from res/drawable-mdpi/play.png rename to app/src/main/res/drawable-mdpi/play.png diff --git a/res/drawable-mdpi/play_status.png b/app/src/main/res/drawable-mdpi/play_status.png similarity index 100% rename from res/drawable-mdpi/play_status.png rename to app/src/main/res/drawable-mdpi/play_status.png diff --git a/res/drawable-mdpi/stop.png b/app/src/main/res/drawable-mdpi/stop.png similarity index 100% rename from res/drawable-mdpi/stop.png rename to app/src/main/res/drawable-mdpi/stop.png diff --git a/res/drawable/backrepeat.xml b/app/src/main/res/drawable/backrepeat.xml similarity index 100% rename from res/drawable/backrepeat.xml rename to app/src/main/res/drawable/backrepeat.xml diff --git a/res/drawable/bg_gradient.xml b/app/src/main/res/drawable/bg_gradient.xml similarity index 100% rename from res/drawable/bg_gradient.xml rename to app/src/main/res/drawable/bg_gradient.xml diff --git a/res/drawable/text_button_bg.xml b/app/src/main/res/drawable/text_button_bg.xml similarity index 100% rename from res/drawable/text_button_bg.xml rename to app/src/main/res/drawable/text_button_bg.xml diff --git a/res/drawable/text_button_pressed.xml b/app/src/main/res/drawable/text_button_pressed.xml similarity index 100% rename from res/drawable/text_button_pressed.xml rename to app/src/main/res/drawable/text_button_pressed.xml diff --git a/res/drawable/text_button_selected.xml b/app/src/main/res/drawable/text_button_selected.xml similarity index 100% rename from res/drawable/text_button_selected.xml rename to app/src/main/res/drawable/text_button_selected.xml diff --git a/res/drawable/white_down_box.xml b/app/src/main/res/drawable/white_down_box.xml similarity index 100% rename from res/drawable/white_down_box.xml rename to app/src/main/res/drawable/white_down_box.xml diff --git a/res/drawable/white_up_box.xml b/app/src/main/res/drawable/white_up_box.xml similarity index 100% rename from res/drawable/white_up_box.xml rename to app/src/main/res/drawable/white_up_box.xml diff --git a/res/drawable/whitebox.xml b/app/src/main/res/drawable/whitebox.xml similarity index 100% rename from res/drawable/whitebox.xml rename to app/src/main/res/drawable/whitebox.xml diff --git a/res/layout/about.xml b/app/src/main/res/layout/about.xml similarity index 100% rename from res/layout/about.xml rename to app/src/main/res/layout/about.xml diff --git a/res/layout/inset_list.xml b/app/src/main/res/layout/inset_list.xml similarity index 91% rename from res/layout/inset_list.xml rename to app/src/main/res/layout/inset_list.xml index 7bdcdd8..5e5961b 100644 --- a/res/layout/inset_list.xml +++ b/app/src/main/res/layout/inset_list.xml @@ -16,7 +16,7 @@ android:background="@drawable/whitebox" android:gravity="center" android:visibility="gone" - android:textAppearance="?android:attr/textAppearanceMedium" + /> diff --git a/res/layout/main.xml b/app/src/main/res/layout/main.xml similarity index 68% rename from res/layout/main.xml rename to app/src/main/res/layout/main.xml index d260ae8..c16a132 100644 --- a/res/layout/main.xml +++ b/app/src/main/res/layout/main.xml @@ -1,6 +1,7 @@ @@ -68,7 +69,7 @@ - + +