Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Custom Android Download Service - providing progress notification row per file

I would like to be able to show multiple downloads of files in the notification bar which can also be cancelled.

I have implemented a custom Service the performs multiple downloads in parallel using AsyncTasks. OnPublishProgress I'm trying to update the individual rows in the notification bar to show the download progress for each file. For two solid days I've been trying to fix issues with the rows flickering, swapping order and sometimes just being blank or only updating one row. Also, tapping the row to cancel routine doesn't always work.

Here is my code:

    protected void showProgressNotification(final File item, int progress, boolean isDownloading) {
    String message = null;
    int smallIcon = 0;
    Bitmap largeIcon = null;
    int flags = 0;
    flags |= Notification.FLAG_ONGOING_EVENT; 
    //flags |= Notification.FLAG_FOREGROUND_SERVICE;
    //flags |= Notification.FLAG_ONLY_ALERT_ONCE;
    //flags |= Notification.FLAG_AUTO_CANCEL; 

    NotificationCompat.Builder builder =
            new NotificationCompat.Builder(getApplicationContext());
    builder.setAutoCancel(true);

    if (progress == 100) {
        largeIcon = BitmapFactory.decodeResource(getResources(),
                O2FolderListAdapter.getIconForItem(item, false));
        smallIcon = R.drawable.ic_cloud_upto_date;

        if (isDownloading) {
            message = "Download completed. Tap to clear.";
        } else {
            message = "Upload completed. Tap to clear.";
        }
    } else if (progress >= 0) {
        largeIcon = BitmapFactory.decodeResource(getResources(),
                O2FolderListAdapter.getIconForItem(item, true));
        if (isDownloading) {
            smallIcon = R.drawable.ic_cloud_downloading;
            message = "Downloading: " + progress + "%. Tap to cancel.";
        } else {
            smallIcon = R.drawable.ic_cloud_uploading;
            message = "Uploading: " + progress + "%. Tap to cancel.";
        }
        builder.setProgress(100, progress, false);
    } else {
        largeIcon = BitmapFactory.decodeResource(getResources(),
                O2FolderListAdapter.getIconForItem(item, true));
        smallIcon = R.drawable.ic_cloud_conflict;
        if (isDownloading)
            message = "Cancelled download. Tap to clear.";
        else
            message = "Cancelled upload. Tap to clear.";
    }

    if (mResultIntent == null) {
        mResultIntent = new Intent(getApplicationContext(), CustomDownloadService.class);
        mResultIntent.addFlags(Notification.FLAG_ONGOING_EVENT);
    }
    mResultIntent.putExtra("cancel", item.getPath().hashCode());
    Log.d("O2AbstractDownloadService", "Setup task id " + item.GetPath().hashCode());
    if (mContentIntent == null)
        mContentIntent = PendingIntent.getService(getApplicationContext(), PI_REQ_CODE, mResultIntent, PendingIntent.FLAG_UPDATE_CURRENT);
    builder.setContentIntent(mContentIntent);

    builder.setLargeIcon(largeIcon);
    builder.setSmallIcon(smallIcon);
    builder.setContentTitle(item.GetName());
    builder.setContentText(message);

    //if (progress != 100)
        //builder.addAction(R.drawable.ic_action_dark_cancel, "Cancel", contentIntent);

    final Notification notification = builder.build();
    notification.flags = flags;
    notification.defaults = Notification.DEFAULT_LIGHTS;

    NotificationManager mNotificationManager =
        (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
    // Id allows you to update the notification later on.
    //mNotificationManager.notify(item.getPath().hashCode(), notification);
    //startForeground(item.getPath().hashCode(), notification);

    // only update notification every 100ms (unless cancel or complete)
    long notificationDelay = 100;
    long now = System.currentTimeMillis();
    if (mFutureCallTime == 0 || now > mFutureCallTime || progress == -1 || progress == 100) {
        startForeground(item.getPath().hashCode(), notification);
            //mNotificationManager.notify(item.GetPath().hashCode(), notification);
    } else
        Log.d("CustomDownloadService", "Called too often to notification");

    mFutureCallTime = now + notificationDelay;
}

So I'm trying to setup the action to call the Service when tapping on the notification, passing the id of the file to cancel the download. Can anyone see what I'm doing wrong? Is what I'm after actually possible? On a Xoom tablet the notifications flicker a lot, but no so often on the Nexus 7. All devices end up swapping rows about constantly which means it is virtually impossible to cancel the download you want.

Any advice would be greatly appreciated.

UPDATE 1: I think this may be causing one of my issues: Android Service.startForeground does NOT respect notification id uniqueness

UPDATE 2: The swapping out issue was fixed by calling builder.setWhen(fixedTime). Obviously, the new dateTime was causing the rows to reorder each time it was refreshed. Just need to fix the flickering on Xoom and the 'Tap to Cancel' feature.

UPDATE 3: The flickering on Xoom was fixed with limiting the calls to refresh. The code at the end prevents the notification from being updated more than once ever 100ms. The remaining issues are to do with cancelling. The tap to cancel works the first time but doesn't work on subsequent files. Also I can't clear the rows.

UPDATE 4: The single cancel issue was cause by the resultIntent field being at class level. When I created a new one each time I refreshed the notification, the ids tied up. I also changed the flag to Notification.FLAG_ONLY_ALERT_ONCE only and only used .notify() and not startForeground().

like image 933
elprl Avatar asked Oct 26 '12 13:10

elprl


1 Answers

All issues were fixed. I've added the updates in my original post. In summary: 1) Be careful with builder.setWhen(fixedTime). 2) Don't refresh more than once every 100ms 3) Set the correct flags.

like image 196
elprl Avatar answered Sep 16 '22 11:09

elprl