summaryrefslogtreecommitdiff
path: root/src/de/danoeh/antennapod/asynctask
diff options
context:
space:
mode:
Diffstat (limited to 'src/de/danoeh/antennapod/asynctask')
-rw-r--r--src/de/danoeh/antennapod/asynctask/DownloadObserver.java150
-rw-r--r--src/de/danoeh/antennapod/asynctask/FlattrClickWorker.java395
-rw-r--r--src/de/danoeh/antennapod/asynctask/FlattrStatusFetcher.java47
3 files changed, 491 insertions, 101 deletions
diff --git a/src/de/danoeh/antennapod/asynctask/DownloadObserver.java b/src/de/danoeh/antennapod/asynctask/DownloadObserver.java
new file mode 100644
index 000000000..26e405615
--- /dev/null
+++ b/src/de/danoeh/antennapod/asynctask/DownloadObserver.java
@@ -0,0 +1,150 @@
+package de.danoeh.antennapod.asynctask;
+
+import android.app.Activity;
+import android.content.*;
+import android.os.Handler;
+import android.os.IBinder;
+import android.util.Log;
+import de.danoeh.antennapod.AppConfig;
+import de.danoeh.antennapod.service.download.DownloadService;
+import de.danoeh.antennapod.service.download.Downloader;
+
+import java.util.List;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * Provides access to the DownloadService's list of items that are currently being downloaded.
+ * The DownloadObserver object should be created in the activity's onCreate() method. resume() and pause()
+ * should be called in the activity's onResume() and onPause() methods
+ */
+public class DownloadObserver {
+ private static final String TAG = "DownloadObserver";
+
+ /**
+ * Time period between update notifications.
+ */
+ public static final int WAITING_INTERVAL_MS = 1000;
+
+ private final Activity activity;
+ private final Handler handler;
+ private final Callback callback;
+
+ private DownloadService downloadService = null;
+ private AtomicBoolean mIsBound = new AtomicBoolean(false);
+
+ private Thread refresherThread;
+ private AtomicBoolean refresherThreadRunning = new AtomicBoolean(false);
+
+
+ /**
+ * Creates a new download observer.
+ *
+ * @param activity Used for registering receivers
+ * @param handler All callback methods are executed on this handler. The handler MUST run on the GUI thread.
+ * @param callback Callback methods for posting content updates
+ * @throws java.lang.IllegalArgumentException if one of the arguments is null.
+ */
+ public DownloadObserver(Activity activity, Handler handler, Callback callback) {
+ if (activity == null) throw new IllegalArgumentException("activity = null");
+ if (handler == null) throw new IllegalArgumentException("handler = null");
+ if (callback == null) throw new IllegalArgumentException("callback = null");
+
+ this.activity = activity;
+ this.handler = handler;
+ this.callback = callback;
+ }
+
+ public void onResume() {
+ if (AppConfig.DEBUG) Log.d(TAG, "DownloadObserver resumed");
+ activity.registerReceiver(contentChangedReceiver, new IntentFilter(DownloadService.ACTION_DOWNLOADS_CONTENT_CHANGED));
+ activity.bindService(new Intent(activity, DownloadService.class), mConnection, 0);
+ }
+
+ public void onPause() {
+ if (AppConfig.DEBUG) Log.d(TAG, "DownloadObserver paused");
+ activity.unregisterReceiver(contentChangedReceiver);
+ activity.unbindService(mConnection);
+ stopRefresher();
+ }
+
+ private BroadcastReceiver contentChangedReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ callback.onContentChanged();
+ startRefresher();
+ }
+ };
+
+ public interface Callback {
+ void onContentChanged();
+
+ void onDownloadDataAvailable(List<Downloader> downloaderList);
+ }
+
+ private ServiceConnection mConnection = new ServiceConnection() {
+ public void onServiceDisconnected(ComponentName className) {
+ downloadService = null;
+ mIsBound.set(false);
+ stopRefresher();
+ Log.i(TAG, "Closed connection with DownloadService.");
+ }
+
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ downloadService = ((DownloadService.LocalBinder) service)
+ .getService();
+ mIsBound.set(true);
+ if (AppConfig.DEBUG)
+ Log.d(TAG, "Connection to service established");
+ List<Downloader> downloaderList = downloadService.getDownloads();
+ if (downloaderList != null && !downloaderList.isEmpty()) {
+ callback.onDownloadDataAvailable(downloaderList);
+ startRefresher();
+ }
+ }
+ };
+
+ private void stopRefresher() {
+ if (refresherThread != null) {
+ refresherThread.interrupt();
+ }
+ }
+
+ private void startRefresher() {
+ if (refresherThread == null || refresherThread.isInterrupted()) {
+ refresherThread = new Thread(new RefresherThread());
+ refresherThread.start();
+ }
+ }
+
+ private class RefresherThread implements Runnable {
+
+ public void run() {
+ refresherThreadRunning.set(true);
+ while (!Thread.interrupted()) {
+ try {
+ Thread.sleep(WAITING_INTERVAL_MS);
+ } catch (InterruptedException e) {
+ Log.d(TAG, "Refresher thread was interrupted");
+ }
+ if (mIsBound.get()) {
+ postUpdate();
+ }
+ }
+ refresherThreadRunning.set(false);
+ }
+
+ private void postUpdate() {
+ handler.post(new Runnable() {
+ @Override
+ public void run() {
+ callback.onContentChanged();
+ List<Downloader> downloaderList = downloadService.getDownloads();
+ if (downloaderList == null || downloaderList.isEmpty()) {
+ Thread.currentThread().interrupt();
+ }
+ }
+ });
+ }
+ }
+
+}
diff --git a/src/de/danoeh/antennapod/asynctask/FlattrClickWorker.java b/src/de/danoeh/antennapod/asynctask/FlattrClickWorker.java
index 975aa5efe..3034bbaff 100644
--- a/src/de/danoeh/antennapod/asynctask/FlattrClickWorker.java
+++ b/src/de/danoeh/antennapod/asynctask/FlattrClickWorker.java
@@ -1,115 +1,308 @@
package de.danoeh.antennapod.asynctask;
-import org.shredzone.flattr4j.exception.FlattrException;
-
import android.annotation.SuppressLint;
+import android.app.Notification;
+import android.app.NotificationManager;
import android.app.ProgressDialog;
import android.content.Context;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
import android.os.AsyncTask;
+import android.support.v4.app.NotificationCompat;
import android.util.Log;
import android.widget.Toast;
import de.danoeh.antennapod.AppConfig;
import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.storage.DBReader;
+import de.danoeh.antennapod.storage.DBWriter;
+import de.danoeh.antennapod.util.flattr.FlattrThing;
import de.danoeh.antennapod.util.flattr.FlattrUtils;
-/** Performs a click action in a background thread. */
-
-public class FlattrClickWorker extends AsyncTask<Void, Void, Void> {
- protected static final String TAG = "FlattrClickWorker";
- protected Context context;
- protected String url;
- protected String errorMsg;
- protected int exitCode;
- protected ProgressDialog progDialog;
-
- protected final static int SUCCESS = 0;
- protected final static int NO_TOKEN = 1;
- protected final static int FLATTR_ERROR = 2;
-
- public FlattrClickWorker(Context context, String url) {
- super();
- this.context = context;
- this.url = url;
- exitCode = SUCCESS;
- errorMsg = "";
- }
-
- protected void onNoAccessToken() {
- Log.w(TAG, "No access token was available");
- if (url.equals(FlattrUtils.APP_URL)) {
- FlattrUtils.showNoTokenDialog(context, FlattrUtils.APP_LINK);
- } else {
- FlattrUtils.showNoTokenDialog(context, url);
- }
- }
-
- protected void onFlattrError() {
- FlattrUtils.showErrorDialog(context, errorMsg);
- }
-
- protected void onSuccess() {
- Toast toast = Toast.makeText(context.getApplicationContext(),
- R.string.flattr_click_success, Toast.LENGTH_LONG);
- toast.show();
- }
-
- protected void onSetupProgDialog() {
- progDialog = new ProgressDialog(context);
- progDialog.setMessage(context.getString(R.string.flattring_label));
- progDialog.setIndeterminate(true);
- progDialog.setCancelable(false);
- progDialog.show();
- }
-
- @Override
- protected void onPostExecute(Void result) {
- if (AppConfig.DEBUG) Log.d(TAG, "Exit code was " + exitCode);
- if (progDialog != null) {
- progDialog.dismiss();
- }
- switch (exitCode) {
- case NO_TOKEN:
- onNoAccessToken();
- break;
- case FLATTR_ERROR:
- onFlattrError();
- break;
- case SUCCESS:
- onSuccess();
- break;
- }
- }
-
- @Override
- protected void onPreExecute() {
- onSetupProgDialog();
- }
-
- @Override
- protected Void doInBackground(Void... params) {
- if (AppConfig.DEBUG) Log.d(TAG, "Starting background work");
- if (FlattrUtils.hasToken()) {
- try {
- FlattrUtils.clickUrl(context, url);
- } catch (FlattrException e) {
- e.printStackTrace();
- exitCode = FLATTR_ERROR;
- errorMsg = e.getMessage();
- }
- } else {
- exitCode = NO_TOKEN;
- }
- return null;
- }
-
- @SuppressLint("NewApi")
- public void executeAsync() {
- FlattrUtils.hasToken();
- if (android.os.Build.VERSION.SDK_INT > android.os.Build.VERSION_CODES.GINGERBREAD_MR1) {
- executeOnExecutor(THREAD_POOL_EXECUTOR);
- } else {
- execute();
- }
- }
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.Executor;
+
+/**
+ * Performs a click action in a background thread.
+ */
+
+public class FlattrClickWorker extends AsyncTask<Void, String, Void> {
+ protected static final String TAG = "FlattrClickWorker";
+ protected Context context;
+
+ private final int NOTIFICATION_ID = 4;
+
+ protected String errorMsg;
+ protected int exitCode;
+ protected ArrayList<String> flattrd;
+ protected ArrayList<String> flattr_failed;
+
+
+ protected NotificationCompat.Builder notificationCompatBuilder;
+ private Notification.BigTextStyle notificationBuilder;
+ protected NotificationManager notificationManager;
+
+ protected ProgressDialog progDialog;
+
+ protected final static int EXIT_DEFAULT = 0;
+ protected final static int NO_TOKEN = 1;
+ protected final static int ENQUEUED = 2;
+ protected final static int NO_THINGS = 3;
+
+ public final static int ENQUEUE_ONLY = 1;
+ public final static int FLATTR_TOAST = 2;
+ public static final int FLATTR_NOTIFICATION = 3;
+
+ private int run_mode = FLATTR_NOTIFICATION;
+
+ private FlattrThing extra_flattr_thing; // additional urls to flattr that do *not* originate from the queue
+
+ /**
+ * @param context
+ * @param run_mode can be one of ENQUEUE_ONLY, FLATTR_TOAST and FLATTR_NOTIFICATION
+ */
+ public FlattrClickWorker(Context context, int run_mode) {
+ this(context);
+ this.run_mode = run_mode;
+ }
+
+ public FlattrClickWorker(Context context) {
+ super();
+ this.context = context;
+ exitCode = EXIT_DEFAULT;
+
+ flattrd = new ArrayList<String>();
+ flattr_failed = new ArrayList<String>();
+
+ errorMsg = "";
+ }
+
+ /* only used in PreferencesActivity for flattring antennapod itself,
+ * can't really enqueue this thing
+ */
+ public FlattrClickWorker(Context context, FlattrThing thing) {
+ this(context);
+ extra_flattr_thing = thing;
+ run_mode = FLATTR_TOAST;
+ Log.d(TAG, "Going to flattr special thing that is not in the queue: " + thing.getTitle());
+ }
+
+ protected void onNoAccessToken() {
+ Log.w(TAG, "No access token was available");
+ }
+
+ protected void onFlattrError() {
+ FlattrUtils.showErrorDialog(context, errorMsg);
+ }
+
+ protected void onFlattred() {
+ String notificationTitle = context.getString(R.string.flattrd_label);
+ String notificationText = "", notificationSubText = "", notificationBigText = "";
+
+ // text for successfully flattred items
+ if (flattrd.size() == 1)
+ notificationText = String.format(context.getString(R.string.flattr_click_success));
+ else if (flattrd.size() > 1) // flattred pending items from queue
+ notificationText = String.format(context.getString(R.string.flattr_click_success_count, flattrd.size()));
+
+ if (flattrd.size() > 0) {
+ String acc = "";
+ for (String s : flattrd)
+ acc += s + '\n';
+ acc = acc.substring(0, acc.length() - 2);
+
+ notificationBigText = String.format(context.getString(R.string.flattr_click_success_queue), acc);
+ }
+
+ // add text for failures
+ if (flattr_failed.size() > 0) {
+ notificationTitle = context.getString(R.string.flattrd_failed_label);
+ notificationText = String.format(context.getString(R.string.flattr_click_failure_count), flattr_failed.size())
+ + " " + notificationText;
+
+ notificationSubText = flattr_failed.get(0);
+
+ String acc = "";
+ for (String s : flattr_failed)
+ acc += s + '\n';
+ acc = acc.substring(0, acc.length() - 2);
+
+ notificationBigText = String.format(context.getString(R.string.flattr_click_failure), acc)
+ + "\n" + notificationBigText;
+ }
+
+ Log.d(TAG, "Going to post notification: " + notificationBigText);
+
+ notificationManager.cancel(NOTIFICATION_ID);
+
+ if (run_mode == FLATTR_NOTIFICATION || flattr_failed.size() > 0) {
+ if (android.os.Build.VERSION.SDK_INT >= 16) {
+ notificationBuilder = new Notification.BigTextStyle(
+ new Notification.Builder(context)
+ .setOngoing(false)
+ .setContentTitle(notificationTitle)
+ .setContentText(notificationText)
+ .setSubText(notificationSubText)
+ .setSmallIcon(R.drawable.stat_notify_sync))
+ .bigText(notificationText + "\n" + notificationBigText);
+ notificationManager.notify(NOTIFICATION_ID, notificationBuilder.build());
+ } else {
+ notificationCompatBuilder = new NotificationCompat.Builder(context) // need new notificationBuilder and cancel/renotify to get rid of progress bar
+ .setContentTitle(notificationTitle)
+ .setContentText(notificationText)
+ .setSubText(notificationBigText)
+ .setTicker(notificationTitle)
+ .setSmallIcon(R.drawable.stat_notify_sync)
+ .setOngoing(false);
+ notificationManager.notify(NOTIFICATION_ID, notificationCompatBuilder.build());
+ }
+ } else if (run_mode == FLATTR_TOAST) {
+ Toast.makeText(context.getApplicationContext(),
+ notificationText,
+ Toast.LENGTH_LONG)
+ .show();
+ }
+ }
+
+ protected void onEnqueue() {
+ Toast.makeText(context.getApplicationContext(),
+ R.string.flattr_click_enqueued,
+ Toast.LENGTH_LONG)
+ .show();
+ }
+
+ protected void onSetupNotification() {
+ if (android.os.Build.VERSION.SDK_INT >= 16) {
+ notificationBuilder = new Notification.BigTextStyle(
+ new Notification.Builder(context)
+ .setContentTitle(context.getString(R.string.flattring_label))
+ .setAutoCancel(true)
+ .setSmallIcon(R.drawable.stat_notify_sync)
+ .setProgress(0, 0, true)
+ .setOngoing(true));
+ } else {
+ notificationCompatBuilder = new NotificationCompat.Builder(context)
+ .setContentTitle(context.getString(R.string.flattring_label))
+ .setAutoCancel(true)
+ .setSmallIcon(R.drawable.stat_notify_sync)
+ .setProgress(0, 0, true)
+ .setOngoing(true);
+ }
+
+ notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
+ }
+
+ @Override
+ protected void onPostExecute(Void result) {
+ if (AppConfig.DEBUG) Log.d(TAG, "Exit code was " + exitCode);
+
+ switch (exitCode) {
+ case NO_TOKEN:
+ notificationManager.cancel(NOTIFICATION_ID);
+ onNoAccessToken();
+ break;
+ case ENQUEUED:
+ onEnqueue();
+ break;
+ case EXIT_DEFAULT:
+ onFlattred();
+ break;
+ case NO_THINGS: // FlattrClickWorker called automatically somewhere to empty flattr queue
+ notificationManager.cancel(NOTIFICATION_ID);
+ break;
+ }
+ }
+
+ @Override
+ protected void onPreExecute() {
+ onSetupNotification();
+ }
+
+ private static boolean haveInternetAccess(Context context) {
+ ConnectivityManager cm =
+ (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
+
+ NetworkInfo networkInfo = cm.getActiveNetworkInfo();
+ return (networkInfo != null && networkInfo.isConnectedOrConnecting());
+ }
+
+ @Override
+ protected Void doInBackground(Void... params) {
+ if (AppConfig.DEBUG) Log.d(TAG, "Starting background work");
+
+ exitCode = EXIT_DEFAULT;
+
+ if (!FlattrUtils.hasToken()) {
+ exitCode = NO_TOKEN;
+ } else if (DBReader.getFlattrQueueEmpty(context) && extra_flattr_thing == null) {
+ exitCode = NO_THINGS;
+ } else if (!haveInternetAccess(context) || run_mode == ENQUEUE_ONLY) {
+ exitCode = ENQUEUED;
+ } else {
+ List<FlattrThing> flattrList = DBReader.getFlattrQueue(context);
+ Log.d(TAG, "flattrQueue processing list with " + flattrList.size() + " items.");
+
+ if (extra_flattr_thing != null)
+ flattrList.add(extra_flattr_thing);
+
+ flattrd.ensureCapacity(flattrList.size());
+
+ for (FlattrThing thing : flattrList) {
+ try {
+ Log.d(TAG, "flattrQueue processing " + thing.getTitle() + " " + thing.getPaymentLink());
+ publishProgress(String.format(context.getString(R.string.flattring_thing), thing.getTitle()));
+
+ thing.getFlattrStatus().setUnflattred(); // pop from queue to prevent unflattrable things from getting stuck in flattr queue infinitely
+
+ FlattrUtils.clickUrl(context, thing.getPaymentLink());
+ flattrd.add(thing.getTitle());
+
+ thing.getFlattrStatus().setFlattred();
+ } catch (Exception e) {
+ Log.d(TAG, "flattrQueue processing exception at item " + thing.getTitle() + " " + e.getMessage());
+ flattr_failed.ensureCapacity(flattrList.size());
+ flattr_failed.add(thing.getTitle() + ": " + e.getMessage());
+ }
+ Log.d(TAG, "flattrQueue processing - going to write thing back to db with flattr_status " + Long.toString(thing.getFlattrStatus().toLong()));
+ DBWriter.setFlattredStatus(context, thing, false);
+ }
+
+ }
+
+ return null;
+ }
+
+ @Override
+ protected void onProgressUpdate(String... names) {
+ if (android.os.Build.VERSION.SDK_INT >= 16) {
+ notificationBuilder.setBigContentTitle(names[0]);
+ notificationManager.notify(NOTIFICATION_ID, notificationBuilder.build());
+ } else {
+ notificationCompatBuilder.setContentText(names[0]);
+ notificationManager.notify(NOTIFICATION_ID, notificationCompatBuilder.build());
+ }
+ }
+
+ @SuppressLint("NewApi")
+ public void executeAsync() {
+ FlattrUtils.hasToken();
+ if (android.os.Build.VERSION.SDK_INT > android.os.Build.VERSION_CODES.GINGERBREAD_MR1) {
+ executeOnExecutor(THREAD_POOL_EXECUTOR);
+ } else {
+ execute();
+ }
+ }
+
+ public void executeSync() {
+ class DirectExecutor implements Executor {
+ public void execute(Runnable r) {
+ r.run();
+ }
+ }
+ FlattrUtils.hasToken();
+ executeOnExecutor(new DirectExecutor());
+
+ }
+
}
diff --git a/src/de/danoeh/antennapod/asynctask/FlattrStatusFetcher.java b/src/de/danoeh/antennapod/asynctask/FlattrStatusFetcher.java
new file mode 100644
index 000000000..4974c6b56
--- /dev/null
+++ b/src/de/danoeh/antennapod/asynctask/FlattrStatusFetcher.java
@@ -0,0 +1,47 @@
+package de.danoeh.antennapod.asynctask;
+
+import android.content.Context;
+import android.util.Log;
+import de.danoeh.antennapod.AppConfig;
+import de.danoeh.antennapod.storage.DBWriter;
+import de.danoeh.antennapod.util.flattr.FlattrUtils;
+import org.shredzone.flattr4j.exception.FlattrException;
+import org.shredzone.flattr4j.model.Flattr;
+
+import java.util.List;
+import java.util.concurrent.ExecutionException;
+
+/**
+ * Fetch list of flattred things and flattr status in database in a background thread.
+ */
+
+public class FlattrStatusFetcher extends Thread {
+ protected static final String TAG = "FlattrStatusFetcher";
+ protected Context context;
+
+ public FlattrStatusFetcher(Context context) {
+ super();
+ this.context = context;
+ }
+
+ @Override
+ public void run() {
+ if (AppConfig.DEBUG) Log.d(TAG, "Starting background work: Retrieving Flattr status");
+
+ Thread.currentThread().setPriority(Thread.MIN_PRIORITY);
+
+ try {
+ List<Flattr> flattredThings = FlattrUtils.retrieveFlattredThings();
+ DBWriter.setFlattredStatus(context, flattredThings).get();
+ } catch (FlattrException e) {
+ e.printStackTrace();
+ Log.d(TAG, "flattrQueue exception retrieving list with flattred items " + e.getMessage());
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ } catch (ExecutionException e) {
+ e.printStackTrace();
+ }
+
+ if (AppConfig.DEBUG) Log.d(TAG, "Finished background work: Retrieved Flattr status");
+ }
+}