package se.slide.sgu;
import android.annotation.SuppressLint;
import android.app.Notification;
import android.app.Notification.Builder;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources.NotFoundException;
import android.os.AsyncTask;
import android.os.IBinder;
import android.preference.PreferenceManager;
import android.util.Base64;
import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.impl.client.DefaultHttpClient;
import org.developerworks.android.FeedParser;
import org.developerworks.android.FeedParserFactory;
import org.developerworks.android.Message;
import org.developerworks.android.ParserType;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.xmlpull.v1.XmlPullParserException;
import se.slide.sgu.db.DatabaseManager;
import se.slide.sgu.model.Content;
import se.slide.sgu.model.Episode;
import se.slide.sgu.model.Item;
import se.slide.sgu.model.Section;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
// Get context in a service? http://stackoverflow.com/questions/987072/using-application-context-everywhere
public class DownloaderService extends Service {
private final String TAG = "DownloaderService";
public static final String ACTION_DOWNLOAD_STARTED = "se.slide.sgu.intent.action.DOWNLOAD_STARTED";
public static final String ACTION_DOWNLOAD_FINISHED = "se.slide.sgu.intent.action.DOWNLOAD_FINISHED";
public static final String EXTRA_USER_INITIATED = "se.slide.sgu.intent.extra.USER_INITIATED";
public static final String EXTRA_DOWNLOAD_STATE = "se.slide.sgu.intent.extra.DOWNLOAD_STATE";
private final int NOTIFICATION_ID = 13;
private final int NOTIFICATION_NEW = 14;
private MetadataAsyncTask mMetadataAsyncTask = null;
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
MyLog.v(TAG, "Started DownloaderService");
DatabaseManager.init(this);
GlobalContext.INSTANCE.init(this);
ContentDownloadManager.INSTANCE.init(this);
String username = PreferenceManager.getDefaultSharedPreferences(this).getString("username", null);
String password = PreferenceManager.getDefaultSharedPreferences(this).getString("password", null);
if (username == null || password == null) {
MyLog.v(TAG, "Username or password is null, stopping service");
stopSelf();
return START_NOT_STICKY;
}
// Are we already running?
if (mMetadataAsyncTask != null) {
MyLog.v(TAG, "AsyncTask is not null which means we're already running, stop new request");
return START_NOT_STICKY;
}
// Is this automatic or user initiated?
boolean manuallyStarted = false;
manuallyStarted = intent.getBooleanExtra(EXTRA_USER_INITIATED, false);
MyLog.v(TAG, "Is manually started: " + manuallyStarted);
// Let our activity know we have started
Intent i = new Intent();
i.setAction(ACTION_DOWNLOAD_STARTED);
sendBroadcast(i);
long lastEpisodeInMs = PreferenceManager.getDefaultSharedPreferences(this).getLong("last_episode_in_ms", 0L);
boolean autoDownload = PreferenceManager.getDefaultSharedPreferences(this).getBoolean("auto_download", false);
// Start metadata download
mMetadataAsyncTask = new MetadataAsyncTask(username, password, lastEpisodeInMs, autoDownload, manuallyStarted);
mMetadataAsyncTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
return START_NOT_STICKY;
}
private class MetadataAsyncTask extends AsyncTask<Void, Void, Boolean> {
private String username;
private String password;
private long lastEpisodeInMs;
private long latestEpisodeFound = 0L;
private boolean autoDownload;
private boolean manuallyStarted = false;
public MetadataAsyncTask(String username, String password, long lastEpisodeInMs, boolean autoDownload, boolean manuallyStarted) {
this.username = username;
this.password = password;
this.lastEpisodeInMs = lastEpisodeInMs;
this.autoDownload = autoDownload;
this.manuallyStarted = manuallyStarted;
}
@Override
protected Boolean doInBackground(Void... params) {
MyLog.v(TAG, "Doing in background: MetadataAsyncTask");
boolean returnValue = true;
StringBuilder builder = new StringBuilder();
HttpUriRequest request = new HttpGet("http://www.x12.se/sgu_metadata.xml");
HttpClient httpclient = new DefaultHttpClient();
try {
HttpResponse response = httpclient.execute(request);
String inputLine;
BufferedReader in = new BufferedReader(new InputStreamReader(response.getEntity().getContent()));
try {
while ((inputLine = in.readLine()) != null) {
builder.append(inputLine);
}
in.close();
} catch (IOException e) {
e.printStackTrace();
returnValue = false;
GlobalContext.INSTANCE.sendExceptionToGoogleAnalytics("While reading inputstream for metadata", Thread.currentThread().getName(), e, false);
}
} catch (ClientProtocolException e) {
e.printStackTrace();
returnValue = false;
GlobalContext.INSTANCE.sendExceptionToGoogleAnalytics("While executing HTTP request, client protocol exception", Thread.currentThread().getName(), e, false);
} catch (IOException e) {
e.printStackTrace();
returnValue = false;
GlobalContext.INSTANCE.sendExceptionToGoogleAnalytics("While executing HTTP request, some IO problem", Thread.currentThread().getName(), e, false);
}
SectionParser parser = new SectionParser();
//List<Section> listOfSection = null;
List<Episode> listOfEpisodes = null;
try {
if (Utils.DEBUG)
listOfEpisodes = parser.parse(getResources().openRawResource(R.raw.sgu_metadata));
else
listOfEpisodes = parser.parse(new ByteArrayInputStream(builder.toString().getBytes("UTF-8")));
} catch (NotFoundException e) {
e.printStackTrace();
returnValue = false;
GlobalContext.INSTANCE.sendExceptionToGoogleAnalytics("While parsing metadata, not found exception", Thread.currentThread().getName(), e, false);
} catch (XmlPullParserException e) {
e.printStackTrace();
returnValue = false;
GlobalContext.INSTANCE.sendExceptionToGoogleAnalytics("While parsing metadata, xml pull parser exception", Thread.currentThread().getName(), e, false);
} catch (IOException e) {
e.printStackTrace();
returnValue = false;
GlobalContext.INSTANCE.sendExceptionToGoogleAnalytics("While parsing metadata, IO exception", Thread.currentThread().getName(), e, false);
}
if (listOfEpisodes != null) {
// Clear old items
DatabaseManager.getInstance().removeSections();
DatabaseManager.getInstance().removeEpisode();
DatabaseManager.getInstance().removeItems();
DatabaseManager.getInstance().removeQuotes();
DatabaseManager.getInstance().removeGuests();
DatabaseManager.getInstance().removeLinks();
for (Episode episode : listOfEpisodes) {
DatabaseManager.getInstance().addSections(episode.listOfSection);
DatabaseManager.getInstance().addGuests(episode.listOfGuests);
DatabaseManager.getInstance().addQuote(episode.quote);
DatabaseManager.getInstance().addItems(episode.listOfItem);
for (Item item : episode.listOfItem) {
DatabaseManager.getInstance().addLinks(item.listOfLinks);
}
for (Section section : episode.listOfSection) {
DatabaseManager.getInstance().addLinks(section.listOfLinks);
}
}
DatabaseManager.getInstance().addEpisodes(listOfEpisodes);
}
return returnValue;
}
@Override
protected void onPostExecute(Boolean result) {
super.onPostExecute(result);
if (!result){
// Send analytics info since we could not get meatdata
}
// Start RSS download
new DownloadAsyncTask(username, password, lastEpisodeInMs, autoDownload, manuallyStarted).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
}
private class DownloadAsyncTask extends AsyncTask<Void, Void, Boolean> {
private String username;
private String password;
private long lastEpisodeInMs;
private long latestEpisodeFound = 0L;
private boolean autoDownload;
private boolean manuallyStarted = false;
private boolean newContent = false;
public DownloadAsyncTask(String username, String password, long lastEpisodeInMs, boolean autoDownload, boolean manuallyStarted) {
this.username = username;
this.password = password;
this.lastEpisodeInMs = lastEpisodeInMs;
this.autoDownload = autoDownload;
this.manuallyStarted = manuallyStarted;
}
@Override
protected Boolean doInBackground(Void... params) {
MyLog.v(TAG, "Doing in background: DownloadAsyncTask");
boolean returnValue = true;
StringBuilder builder = new StringBuilder();
//DefaultHttpClient httpclient = new DefaultHttpClient();
/*
httpclient.getParams().setParameter(ClientPNames.COOKIE_POLICY, CookiePolicy.BROWSER_COMPATIBILITY);
HttpPost httpost = new HttpPost("http://www.theskepticsguide.org/wp-login.php");
List<NameValuePair> nvps = new ArrayList<NameValuePair>();
nvps.add(new BasicNameValuePair("log", username));
nvps.add(new BasicNameValuePair("pwd", password));
nvps.add(new BasicNameValuePair("rememberme", "forever"));
nvps.add(new BasicNameValuePair("redirect_to", "/members-only-content"));
nvps.add(new BasicNameValuePair("wp-submit", "Login"));
try {
httpost.setEntity(new UrlEncodedFormEntity(nvps, HTTP.UTF_8));
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
returnValue = false;
Utils.sendExceptionToGoogleAnalytics(getApplicationContext(), Thread.currentThread().getName(), e, false);
}
*/
HttpUriRequest request = new HttpGet("https://www.theskepticsguide.org/premium");
String credentials = username + ":" + password;
String base64EncodedCredentials = Base64.encodeToString(credentials.getBytes(), Base64.NO_WRAP);
request.addHeader("Authorization", "Basic " + base64EncodedCredentials);
HttpClient httpclient = new DefaultHttpClient();
try {
HttpResponse response = httpclient.execute(request);
String inputLine;
BufferedReader in = new BufferedReader(new InputStreamReader(response.getEntity().getContent()));
try {
while ((inputLine = in.readLine()) != null) {
builder.append(inputLine);
}
in.close();
} catch (IOException e) {
e.printStackTrace();
returnValue = false;
GlobalContext.INSTANCE.sendExceptionToGoogleAnalytics("While reading RSS inputstream, IO exception", Thread.currentThread().getName(), e, false);
}
} catch (ClientProtocolException e) {
e.printStackTrace();
returnValue = false;
GlobalContext.INSTANCE.sendExceptionToGoogleAnalytics("While executing HTTP request, client protocol exception", Thread.currentThread().getName(), e, false);
} catch (IOException e) {
e.printStackTrace();
returnValue = false;
GlobalContext.INSTANCE.sendExceptionToGoogleAnalytics("While executing HTTP request, IO exception", Thread.currentThread().getName(), e, false);
}
// Try to parse the content
try {
parseRss(builder.toString());
} catch (Exception e) {
e.printStackTrace();
returnValue = false;
GlobalContext.INSTANCE.sendExceptionToGoogleAnalytics("While parsing RSS, exception", Thread.currentThread().getName(), e, false);
}
return returnValue;
}
private void parseRss(String rss) throws Exception {
FeedParser parser = FeedParserFactory.getParser(ParserType.ANDROID_SAX, rss);
List<Message> messages = parser.parse();
List<Content> listOfContent = new ArrayList<Content>();
for (Message message : messages) {
Content content = new Content();
content.title = message.getTitle();
content.description = message.getDescription();
content.mp3 = message.getEnclosureUrl().toExternalForm();
content.length = message.getEnclosureLength();
content.published = message.getDateObject();
content.guid = message.getGuId();
listOfContent.add(content);
}
for (Content content : listOfContent) {
Date published = content.published;
// Keep track of the latest episode date
long t = published.getTime();
if (t > latestEpisodeFound)
latestEpisodeFound = t;
if (autoDownload && lastEpisodeInMs != 0 && t > lastEpisodeInMs) {
ContentDownloadManager.INSTANCE.addToDownloadQueue(content.mp3, content.title, content.description, Utils.formatFilename(content.title));
newContent = true;
}
}
DatabaseManager.getInstance().createIfNotExistsContents(listOfContent);
}
/**
* Legacy method for parsing the members content Wordpress-page
*
* @param html
* @throws Exception
*/
private void parseMembersOnlyHtml(String html) throws Exception {
Document doc = Jsoup.parse(html);
Elements elements = doc.select("div.premium");
List<Content> listOfContent = new ArrayList<Content>();
for (int i = 0; i < elements.size(); i++) {
Element e = elements.get(i);
String mp3 = null;
String title = null;
String description = null;
// First get mp3 filename
Elements mp3s = e.select("a[href$=.mp3]");
if (mp3s.size() == 1) {
Element href = mp3s.first();
mp3 = href.attr("href");
}
Elements spanTitles = e.select("h5.section-title span");
if (spanTitles.size() == 1) {
Element span = spanTitles.first();
title = span.text();
}
Elements divDescriptions = e.select("div.podcasts-description");
if (divDescriptions.size() == 1) {
Element div = divDescriptions.first();
description = div.text();
}
if (mp3 == null || title == null || description == null)
continue;
listOfContent.add(new Content(title, description, mp3));
}
DatabaseManager.getInstance().createIfNotExistsContents(listOfContent);
}
@Override
protected void onPostExecute(Boolean result) {
super.onPostExecute(result);
if (result) {
if (!manuallyStarted && newContent) {
Notification notification = GlobalContext.INSTANCE.buildNotification(getString(R.string.download_new_ticker), getString(R.string.download_new_title), getString(R.string.download_new_text));
NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
manager.notify(NOTIFICATION_NEW, notification);
}
GlobalContext.INSTANCE.savePreference("last_episode_in_ms", latestEpisodeFound);
}
else {
Notification notification = GlobalContext.INSTANCE.buildNotification(getString(R.string.download_error_ticker), getString(R.string.download_error_title), getString(R.string.download_error_text));
NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
manager.notify(NOTIFICATION_ID, notification);
}
GlobalContext.INSTANCE.resetContentCache();
Intent intent = new Intent();
intent.setAction(ACTION_DOWNLOAD_FINISHED);
intent.putExtra(DownloaderService.EXTRA_DOWNLOAD_STATE, result);
sendBroadcast(intent);
stopSelf();
}
}
}