Core Android

Display ProgressBar in Notification while Uploading Image

In the previous tutorials, I was explained how to upload the file to the server using Retrofit and RxJava in Android. Most of the people were appreciated that article and request to write an article on display ProgressBar in head-up notification while file uploading to the server. So, Now this article I’m going to explain, how to upload the file to the server with Notification ProgressBar.

As you know the previous article was on File Uploading with Retrofit. So I carried forward previous source code and focus on explaining how to display in progress bar notification while file uploading? So I have recommended my previous article here.

Objective

  • Get an image from camera/gallery
  • While uploading show progress bar in notification bar
  • If due to some network failure notify the user in giving to action button in Notification Bar RETRY or CANCEL
  • In case user hits the retry button the file upload request again lineup.
  • If hit cancels action button then cancel the request and clear the notification.

This article is outdated for this solution. Read our new article is upload manager


1. Add dependency for using EventBus

We are going to use EventBus for simplifies the communication between components. so open build.gradle and add below dependency

implementation 'org.greenrobot:eventbus:3.1.1'

2. Register/UnRegister EventBus Receiver

Open Activity an register EventBus receiver in onCreate methods using below code

EventBus.getDefault().register(this);

And unregister using this

  @Override
    protected void onDestroy() {
        super.onDestroy();
        EventBus.getDefault().unregister(this);
    }

3. Create a Job for Event Bus

Go src folder and create a new class with OnReceiverEvent,java name just like getter and setter

package com.androidwave.fileupload.receiver;

/**
 * Created on : Jan 11, 2019
 */
public class OnReceiverEvent {
  private String mFilePath;

  public OnReceiverEvent(String filePath) {
    this.mFilePath = filePath;
  }

  public String getFilePath() {
    return mFilePath;
  }
}

4. Post job for file uploading using EventBus

Create a new object of OnReceiverEvent and pass file path in the constructor. For now, the object is holding the current file path. Furthermore using EventBus post a job so It notifies all subscribers.

On upload button OnClickListner() post the job like

  @OnClick({ R.id.upload_file_progress })
  public void onViewClicked(View view) {
    switch (view.getId()) {
      case R.id.upload_file_progress:
        EventBus.getDefault().post(new OnReceiverEvent(mImagePresenter.getImage()));
      default:
        break;
    }
  }

5. Now prepare subscribers using annotate for listening OnReceiverEvent

  @Subscribe
  public void onEvent(OnReceiverEvent event) {
  }

6. Create an ongoing notification with progress bar

  @Subscribe
  public void onEvent(OnReceiverEvent event) {
    notificationId = (int) System.currentTimeMillis();
    Context mContext = MainActivity.this;
    Intent resultIntent = new Intent();
    PendingIntent resultPendingIntent = PendingIntent.getActivity(mContext,
        0 /* Request code */, resultIntent,
        PendingIntent.FLAG_UPDATE_CURRENT);

    mBuilder = new NotificationCompat.Builder(mContext, NOTIFICATION_CHANNEL_ID);
    mBuilder.setSmallIcon(R.drawable.ic_stat_notification);
    mBuilder.setColor(ContextCompat.getColor(mContext, R.color.colorPrimary));
    mBuilder.setContentTitle(getString(R.string.uploading))
        .setOngoing(true)
        .setContentText(getString(R.string.in_progress))
        .setContentIntent(resultPendingIntent)
        .setPriority(NotificationCompat.PRIORITY_HIGH);

    mBuilder.setSound(null);
    mBuilder.setVibrate(new long[] { 0L });
    mBuilder.build().flags |= Notification.FLAG_ONGOING_EVENT;
    mBuilder.setWhen(System.currentTimeMillis());
    mNotificationManager =
        (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
      int importance = NotificationManager.IMPORTANCE_HIGH;
      NotificationChannel notificationChannel =
          new NotificationChannel(NOTIFICATION_CHANNEL_ID, NOTIFICATION_CHANNEL_NAME, importance);
      notificationChannel.setDescription("no sound");
      notificationChannel.setSound(null, null);
      notificationChannel.enableLights(false);
      notificationChannel.enableVibration(false);
      mBuilder.setChannelId(NOTIFICATION_CHANNEL_ID);
      mNotificationManager.createNotificationChannel(notificationChannel);
    }
    mBuilder.setProgress(100, 0, false);
    mNotificationManager.notify(notificationId, mBuilder.build());
  }

7. Now Call onFileSelected() on FileUploadPresenter that upload the file to the server

 mUploaderPresenter.onFileSelected(event.getFilePath(), "androidwave", "info@androidwave.com");

As previous example we implemented FileUploaderContract.View in MainActivity, So view have three methods

  1. void setUploadProgress(int progress) Notify progress during file upload
  2. void uploadCompleted() – Call when file upload operation completed
  3. void showErrorMessage(String message) – Call when some error occurred during file upload
7.2 Notify progress during file upload
  @Override
  public void setUploadProgress(int progress) {
    txtProgress.setText("Uploading ..." + String.valueOf(progress));
    mBuilder.setProgress(PROGRESS_MAX, progress, false);
    mNotificationManager.notify(notificationId, mBuilder.build());
    if (progress == 100) {
      mBuilder.setProgress(0, 0, false);
      mBuilder.setContentText(getString(R.string.message_file_uploaded));
    }
  }
7.2 When File Upload Successfully then update the notification
  @Override
  public void uploadCompleted() {
    Toast.makeText(getApplicationContext(), getString(R.string.file_upload_successful),
        Toast.LENGTH_SHORT).show();
    Intent resultIntent = new Intent(MainActivity.this, MainActivity.class);
    updateNotification(getString(R.string.message_upload_success),
        getString(R.string.message_file_uploaded), resultIntent, notificationId);
  }
7.3 When some error occurred during file upload
  @Override
  public void showErrorMessage(String message) {
    Toast.makeText(getApplicationContext(), message, Toast.LENGTH_SHORT).show();
    Intent resultIntent = new Intent();
    retryNotification(getString(R.string.error_upload_falied),
        getString(R.string.messge_upload_failed), resultIntent, notificationId);
  }

8. Create retry notification with action button

During file upload operation is not get succeed then generate a notification with action button

  /**
   * Create and push the notification
   */
  public void retryNotification(String title, String message, Intent resultIntent,
      int notificationId) {
    PendingIntent resultPendingIntent = PendingIntent.getActivity(MainActivity.this,
        0 /* Request code */, resultIntent,
        PendingIntent.FLAG_UPDATE_CURRENT);
    mBuilder = new NotificationCompat.Builder(MainActivity.this, NOTIFICATION_CHANNEL_ID);
    mBuilder.setSmallIcon(R.drawable.ic_stat_notification);
    mBuilder.setColor(ContextCompat.getColor(MainActivity.this, R.color.colorPrimary));
    mBuilder.setContentTitle(title)
        .setContentText(message)
        .setOngoing(true)
        .setContentIntent(resultPendingIntent)
        .setDefaults(NotificationCompat.DEFAULT_ALL)
        .setPriority(NotificationCompat.PRIORITY_HIGH);

    mBuilder.setVibrate(new long[] { 0L });
    mBuilder.setVisibility(NotificationCompat.VISIBILITY_PUBLIC);
    mNotificationManager =
        (NotificationManager) MainActivity.this.getSystemService(Context.NOTIFICATION_SERVICE);
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
      int importance = NotificationManager.IMPORTANCE_HIGH;
      NotificationChannel notificationChannel =
          new NotificationChannel(NOTIFICATION_CHANNEL_ID, NOTIFICATION_CHANNEL_NAME, importance);
      notificationChannel.enableLights(false);
      notificationChannel.enableVibration(false);
      mBuilder.setChannelId(NOTIFICATION_CHANNEL_ID);
      mNotificationManager.createNotificationChannel(notificationChannel);
    }

    Intent retryIntent = new Intent(MainActivity.this, NotificationActionReceiver.class);
    retryIntent.putExtra("notificationId", notificationId);
    retryIntent.putExtra("file_path", mImagePresenter.getImage());
    retryIntent.setAction(ACTION_RETRY);

    Intent clearIntent = new Intent(MainActivity.this, NotificationActionReceiver.class);
    clearIntent.putExtra("notificationId", notificationId);
    clearIntent.putExtra("file_path", mImagePresenter.getImage());
    clearIntent.setAction(ACTION_CLEAR);

    PendingIntent retryPendingIntent =
        PendingIntent.getBroadcast(MainActivity.this, 0, retryIntent, 0);
    PendingIntent clearPendingIntent =
        PendingIntent.getBroadcast(MainActivity.this, 0, clearIntent, 0);
    mBuilder.addAction(android.R.drawable.ic_menu_revert, getString(R.string.btn_retry_not),
        retryPendingIntent);
    mBuilder.addAction(android.R.drawable.ic_menu_revert, getString(R.string.btn_cancel_not),
        clearPendingIntent);
    assert mNotificationManager != null;
    mNotificationManager.notify(notificationId, mBuilder.build());
  }

9. Create a Broadcast Receiver for receiving broadcast event

So we are using two action button Retry and Cancel in the notification. To create a broadcast receiver with name NotificationActionReceiver. onReceive() is receive action and we manage to cancel and Retry action

package com.androidwave.fileupload.receiver;

import android.app.NotificationManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;

import org.greenrobot.eventbus.EventBus;

import java.util.Objects;

  /**
   * Created on : Jan 11, 2019
   */
  public class NotificationActionReceiver extends BroadcastReceiver {
    private static final String TAG = "NotificationActionRecei";
    public static final String ACTION_RETRY = "com.androidwave.ACTION_RETRY";
    public static final String ACTION_CLEAR = "com.androidwave.ACTION_CLEAR";

    @Override
    public void onReceive(Context context, Intent intent) {
      int notificationId = intent.getIntExtra("notificationId", 0);
      String mFilePath = intent.getStringExtra("file_path");
      NotificationManager manager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
      switch (Objects.requireNonNull(intent.getAction())) {
        case ACTION_RETRY:
          manager.cancel(notificationId);
          EventBus.getDefault().post(new OnRetryReceiverEvent(mFilePath));
          break;
        case ACTION_CLEAR:
          manager.cancel(notificationId);
          break;
        default:
          break;
      }


    }
  }

10. Create a class OnRetryReceiverEvent class for retry job

package com.androidwave.fileupload.receiver;

/**
 * Created on : Jan 11, 2019
 */
public class OnRetryReceiverEvent {
  private String mFilePath;

  public OnRetryReceiverEvent(String filePath) {
    this.mFilePath = filePath;
  }

  public String getFilePath() {
    return mFilePath;
  }
}

11. Now register and unregister receiver in MainActivity

Open Activity and register receiver in onCreate()

  mActionReceiver = new NotificationActionReceiver();
  IntentFilter filterRetry = new IntentFilter();
        filterRetry.addAction(ACTION_RETRY);
        filterRetry.addAction(ACTION_CLEAR);
  registerReceiver(mActionReceiver, filterRetry);

In onDestroy() unregister receiver

  @Override
  protected void onDestroy() {
    super.onDestroy();
    unregisterReceiver(mActionReceiver);
  }

Finally, MainActivity looks like

package com.androidwave.fileupload;

import android.Manifest;
import android.app.AlertDialog;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.provider.MediaStore;
import android.support.annotation.Nullable;
import android.support.v4.app.ActivityCompat;
import android.support.v4.app.NotificationCompat;
import android.support.v4.content.ContextCompat;
import android.support.v4.content.FileProvider;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.Toast;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;
import com.androidwave.fileupload.network.FileUploaderContract;
import com.androidwave.fileupload.network.FileUploaderModel;
import com.androidwave.fileupload.network.FileUploaderPresenter;
import com.androidwave.fileupload.network.ServiceGenerator;
import com.androidwave.fileupload.picker.ImageContract;
import com.androidwave.fileupload.picker.ImagePresenter;
import com.androidwave.fileupload.receiver.NotificationActionReceiver;
import com.androidwave.fileupload.receiver.OnReceiverEvent;
import com.androidwave.fileupload.receiver.OnRetryReceiverEvent;
import com.androidwave.fileupload.utils.FileCompressor;
import com.bumptech.glide.Glide;
import com.bumptech.glide.request.RequestOptions;
import com.karumi.dexter.Dexter;
import com.karumi.dexter.MultiplePermissionsReport;
import com.karumi.dexter.PermissionToken;
import com.karumi.dexter.listener.PermissionRequest;
import com.karumi.dexter.listener.multi.MultiplePermissionsListener;
import java.io.File;
import java.io.IOException;
import java.util.Calendar;
import java.util.List;
import java.util.Objects;
import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;

import static com.androidwave.fileupload.receiver.NotificationActionReceiver.ACTION_CLEAR;
import static com.androidwave.fileupload.receiver.NotificationActionReceiver.ACTION_RETRY;

public class MainActivity extends AppCompatActivity
    implements ImageContract.View, FileUploaderContract.View {

  static final int REQUEST_TAKE_PHOTO = 1001;
  static final int REQUEST_GALLERY_PHOTO = 1002;
  private static final int PROGRESS_MAX = 100;
  static String[] permissions =
      new String[] { Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.CAMERA };
  @BindView(R.id.header_cover_image)
  ImageView headerCoverImage;
  @BindView(R.id.user_profile_photo)
  ImageButton userProfilePhoto;
  @BindView(R.id.user_profile_name)
  TextView userProfileName;
  @BindView(R.id.user_profile_short_bio)
  TextView userProfileShortBio;
  @BindView(R.id.profile_layout)
  RelativeLayout profileLayout;
  @BindView(R.id.textViewProgress)
  TextView txtProgress;
  @BindView(R.id.upload_file_progress)
  Button uploadFileProgress;
  @BindView(R.id.btn_upload_file_without_progress)
  Button btnUploadFileWithoutProgress;

  private ImagePresenter mImagePresenter;
  private FileUploaderPresenter mUploaderPresenter;
  private FileCompressor mCompressor;
  File mPhotoFile;

  private NotificationManager mNotificationManager;
  private NotificationCompat.Builder mBuilder;
  int notificationId;

  BroadcastReceiver mActionReceiver;
  public static final String LINK_TYPE = "link_type";
  public static final String LINK_ID = "link_id";

  public static final String NOTIFICATION_CHANNEL_ID = "androidwave_1002";
  public static final String NOTIFICATION_CHANNEL_NAME = "androidwave";

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    ButterKnife.bind(this);
    mImagePresenter = new ImagePresenter(this);
    mUploaderPresenter =
        new FileUploaderPresenter(this, new FileUploaderModel(ServiceGenerator.createService()));
    mCompressor = new FileCompressor(this);
    EventBus.getDefault().register(this);
    mActionReceiver = new NotificationActionReceiver();
    IntentFilter filterRetry = new IntentFilter();
    filterRetry.addAction(ACTION_RETRY);
    filterRetry.addAction(ACTION_CLEAR);
    registerReceiver(mActionReceiver, filterRetry);
  }

  @Override
  public boolean checkPermission() {
    for (String mPermission : permissions) {
      int result = ActivityCompat.checkSelfPermission(this, mPermission);
      if (result == PackageManager.PERMISSION_DENIED) return false;
    }
    return true;
  }

  @Override
  public void showPermissionDialog(boolean isGallery) {
    Dexter.withActivity(this).withPermissions(permissions)
        .withListener(new MultiplePermissionsListener() {
          @Override
          public void onPermissionsChecked(MultiplePermissionsReport report) {
            // check if all permissions are granted
            if (report.areAllPermissionsGranted()) {
              if (isGallery) {
                mImagePresenter.chooseGalleryClick();
              } else {
                mImagePresenter.cameraClick();
              }
            }
            // check for permanent denial of any permission
            if (report.isAnyPermissionPermanentlyDenied()) {
              // show alert dialog navigating to Settings
              showSettingsDialog();
            }
          }

          @Override
          public void onPermissionRationaleShouldBeShown(List<PermissionRequest> permissions,
              PermissionToken token) {
            token.continuePermissionRequest();
          }
        }).withErrorListener(error -> showErrorDialog())
        .onSameThread()
        .check();
  }

  @Override
  public File getFilePath() {
    return getExternalFilesDir(Environment.DIRECTORY_PICTURES);
  }

  @Override
  public void openSettings() {

  }

  @Override
  public void startCamera(File file) {
    Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
    if (takePictureIntent.resolveActivity(getPackageManager()) != null) {
      if (file != null) {
        Uri mPhotoURI = FileProvider.getUriForFile(this,
            BuildConfig.APPLICATION_ID + ".provider", file);
        takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, mPhotoURI);
        mPhotoFile = file;
        startActivityForResult(takePictureIntent, REQUEST_TAKE_PHOTO);
      }
    }
  }

  @Override
  public void chooseGallery() {
    Intent pickPhoto = new Intent(Intent.ACTION_PICK,
        MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
    pickPhoto.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
    startActivityForResult(pickPhoto, REQUEST_GALLERY_PHOTO);
  }

  @Override
  protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if (resultCode == RESULT_OK) {
      if (requestCode == REQUEST_TAKE_PHOTO) {
        try {
          File resultedFile = mCompressor.compressToFile(mPhotoFile);
          mImagePresenter.saveImage(resultedFile.getPath());
          mImagePresenter.showPreview(resultedFile);
        } catch (IOException e) {
          e.printStackTrace();
        }
      } else if (requestCode == REQUEST_GALLERY_PHOTO) {
        Uri selectedImage = data.getData();
        try {
          File resultedFile = mCompressor.compressToFile(
              new File(Objects.requireNonNull(getRealPathFromUri(selectedImage))));
          mImagePresenter.saveImage(resultedFile.getPath());
          mImagePresenter.showPreview(resultedFile);
        } catch (IOException