From cfb7c156462ae4186bc49febf9902accc4a2ae81 Mon Sep 17 00:00:00 2001 From: daniel oeh Date: Sun, 10 Feb 2013 17:20:03 +0100 Subject: [PATCH] Added expandable notifications to download service --- .../service/download/DownloadService.java | 165 +++++++++++++++--- 1 file changed, 140 insertions(+), 25 deletions(-) diff --git a/src/de/danoeh/antennapod/service/download/DownloadService.java b/src/de/danoeh/antennapod/service/download/DownloadService.java index 1e6480cce..ad0bb534a 100644 --- a/src/de/danoeh/antennapod/service/download/DownloadService.java +++ b/src/de/danoeh/antennapod/service/download/DownloadService.java @@ -14,7 +14,12 @@ import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; +import java.util.concurrent.RejectedExecutionHandler; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.ThreadFactory; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; import javax.xml.parsers.ParserConfigurationException; @@ -25,6 +30,7 @@ import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; import android.app.Service; +import android.app.Notification.Builder; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; @@ -99,7 +105,8 @@ public class DownloadService extends Service { private DownloadRequester requester; private FeedManager manager; - private NotificationCompat.Builder notificationBuilder; + private NotificationCompat.Builder notificationCompatBuilder; + private Notification.BigTextStyle notificationBuilder; private int NOTIFICATION_ID = 2; private int REPORT_ID = 3; @@ -114,6 +121,11 @@ public class DownloadService extends Service { private Handler handler; + private NotificationUpdater notificationUpdater; + private ScheduledFuture notificationUpdaterFuture; + private static final int SCHED_EX_POOL_SIZE = 1; + private ScheduledThreadPoolExecutor schedExecutor; + private final IBinder mBinder = new LocalBinder(); public class LocalBinder extends Binder { @@ -176,6 +188,24 @@ public class DownloadService extends Service { return t; } }); + schedExecutor = new ScheduledThreadPoolExecutor(SCHED_EX_POOL_SIZE, + new ThreadFactory() { + + @Override + public Thread newThread(Runnable r) { + Thread t = new Thread(r); + t.setPriority(Thread.MIN_PRIORITY); + return t; + } + }, new RejectedExecutionHandler() { + + @Override + public void rejectedExecution(Runnable r, + ThreadPoolExecutor executor) { + Log.w(TAG, "SchedEx rejected submission of new task"); + } + }); + setupNotificationBuilders(); manager = FeedManager.getInstance(); requester = DownloadRequester.getInstance(); } @@ -194,27 +224,81 @@ public class DownloadService extends Service { unregisterReceiver(downloadQueued); } - private void setupNotification() { + @SuppressLint("NewApi") + private void setupNotificationBuilders() { PendingIntent pIntent = PendingIntent.getActivity(this, 0, new Intent( this, DownloadActivity.class), PendingIntent.FLAG_UPDATE_CURRENT); Bitmap icon = BitmapFactory.decodeResource(getResources(), R.drawable.stat_notify_sync); - notificationBuilder = new NotificationCompat.Builder(this) - .setContentTitle( - getString(R.string.download_notification_title)) - .setContentText( - requester.getNumberOfDownloads() - + getString(R.string.downloads_left)) - .setOngoing(true).setContentIntent(pIntent).setLargeIcon(icon) - .setSmallIcon(R.drawable.stat_notify_sync); - startForeground(NOTIFICATION_ID, notificationBuilder.getNotification()); + if (android.os.Build.VERSION.SDK_INT >= 16) { + notificationBuilder = new Notification.BigTextStyle( + new Notification.Builder(this).setOngoing(true) + .setContentIntent(pIntent).setLargeIcon(icon) + .setSmallIcon(R.drawable.stat_notify_sync)); + } else { + notificationCompatBuilder = new NotificationCompat.Builder(this) + .setOngoing(true).setContentIntent(pIntent) + .setLargeIcon(icon) + .setSmallIcon(R.drawable.stat_notify_sync); + } if (AppConfig.DEBUG) Log.d(TAG, "Notification set up"); } + /** + * Updates the contents of the service's notifications. Should be called + * before setupNotificationBuilders. + */ + @SuppressLint("NewApi") + private Notification updateNotifications() { + String contentTitle = getString(R.string.download_notification_title); + String downloadsLeft = requester.getNumberOfDownloads() + + getString(R.string.downloads_left); + if (android.os.Build.VERSION.SDK_INT >= 16) { + + if (notificationBuilder != null) { + + StringBuilder bigText = null; + for (Downloader downloader : downloads) { + if (bigText == null) { + bigText = new StringBuilder(); + } else { + bigText.append("\n"); + } + if (downloader.getStatus() != null) { + FeedFile f = downloader.getStatus().getFeedFile(); + if (f.getClass() == Feed.class) { + bigText.append("\u2022 " + ((Feed) f).getTitle()); + } else if (f.getClass() == FeedMedia.class) { + FeedMedia media = (FeedMedia) f; + bigText.append("\u2022 " + + media.getItem().getTitle() + + " (" + + downloader.getStatus() + .getProgressPercent() + "%)"); + } + } + } + notificationBuilder.setSummaryText(downloadsLeft); + notificationBuilder.setBigContentTitle(contentTitle); + if (bigText != null) { + notificationBuilder.bigText(bigText.toString()); + } + return notificationBuilder.build(); + } + } else { + if (notificationCompatBuilder != null) { + notificationCompatBuilder.setContentTitle(contentTitle); + notificationCompatBuilder.setContentText(downloadsLeft); + return notificationCompatBuilder.getNotification(); + } + } + return null; + } + private Downloader getDownloader(String downloadUrl) { for (Downloader downloader : downloads) { if (downloader.getStatus().getFeedFile().getDownload_url() @@ -275,7 +359,6 @@ public class DownloadService extends Service { if (AppConfig.DEBUG) Log.d(TAG, "Cancelling shutdown; new download was queued"); shutdownInitiated = false; - setupNotification(); } DownloadRequester requester = DownloadRequester.getInstance(); @@ -340,7 +423,7 @@ public class DownloadService extends Service { queryDownloads(); } } - + @Override protected void onPreExecute() { super.onPreExecute(); @@ -392,9 +475,12 @@ public class DownloadService extends Service { * DownloadService list. */ private void removeDownload(final Downloader d) { - if (AppConfig.DEBUG) Log.d(TAG, "Removing downloader: " + d.getStatus().getFeedFile().getDownload_url()); + if (AppConfig.DEBUG) + Log.d(TAG, "Removing downloader: " + + d.getStatus().getFeedFile().getDownload_url()); boolean rc = downloads.remove(d); - if (AppConfig.DEBUG) Log.d(TAG, "Result of downloads.remove: " + rc); + if (AppConfig.DEBUG) + Log.d(TAG, "Result of downloads.remove: " + rc); DownloadRequester.getInstance().removeDownload( d.getStatus().getFeedFile()); sendBroadcast(new Intent(ACTION_DOWNLOADS_CONTENT_CHANGED)); @@ -502,18 +588,11 @@ public class DownloadService extends Service { Log.d(TAG, "Starting shutdown"); shutdownInitiated = true; updateReport(); + cancelNotificationUpdater(); stopForeground(true); } else { - if (notificationBuilder != null) { - // update notification - notificationBuilder.setContentText(numOfDownloads - + " Downloads left"); - NotificationManager nm = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); - nm.notify(NOTIFICATION_ID, - notificationBuilder.getNotification()); - } else { - setupNotification(); - } + setupNotificationUpdater(); + startForeground(NOTIFICATION_ID, updateNotifications()); } } @@ -848,6 +927,42 @@ public class DownloadService extends Service { } + /** Schedules the notification updater task if it hasn't been scheduled yet. */ + private void setupNotificationUpdater() { + if (AppConfig.DEBUG) + Log.d(TAG, "Setting up notification updater"); + if (notificationUpdater == null) { + notificationUpdater = new NotificationUpdater(); + notificationUpdaterFuture = schedExecutor.scheduleAtFixedRate( + notificationUpdater, 5L, 5L, TimeUnit.SECONDS); + } + } + + private void cancelNotificationUpdater() { + boolean result = false; + if (notificationUpdaterFuture != null) { + result = notificationUpdaterFuture.cancel(true); + } + notificationUpdater = null; + notificationUpdaterFuture = null; + Log.d(TAG, "NotificationUpdater cancelled. Result: " + result); + } + + private class NotificationUpdater implements Runnable { + public void run() { + handler.post(new Runnable() { + @Override + public void run() { + Notification n = updateNotifications(); + if (n != null) { + NotificationManager nm = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); + nm.notify(NOTIFICATION_ID, n); + } + } + }); + } + } + public List getDownloads() { return downloads; }