In this post, we will create a sample app Upload Manager such as Android Download Manager. In this android app tutorial we build a complete solution for file uploading with head up a notification, No matter your app is background or foreground because that time we are using Service Enqueue for file uploading, In case of failure, the user can retry or cancel file upload without open the application. I will try to make a complete solution just like a phone download manager.
Prerequisite
In this article, We are using Retrofit, JobIntentService and Broadcast Receiver, Notification Service. You have a deep knowledge of each one. I have written articles for all major concepts. Read for capture file from Camera & Gallery using FileProvider. For Working with JobIntentService follow this article.
Data Flow of this Upload Manager

Step for implementation Upload Manager
- Create a new project with min SDK 21.
- Add lib dependency in app/build.gradle
- Create a Retrofit instance for calling file upload service
- Forgetting file upload progress let’s creates a CountingRequestBody
- Now create a subclass of JobIntentService
- Furthermore, Create a BroadcastReceiver for listening file upload progress
- Create another BroadcastReceiver with Retry and Cancel action button. While any error occurred during file upload user can retry for file upload
- In MainActivity, We write code for getting the file from camera & gallery using FileProvider for upload file to the server
- Finally, Enqueue the job to JobIntentService.
After following above step we will prepare Upload Manager (Demo App)
1. Create Project
Let move to android studio and create a new project with named FileUploadService. Choose min SDK version 21 and select EmptyActivity template.
Let’s go to res =>value => open string.xml file add some string constant that we are using in this project.
<resources> <string name="app_name">Upload Manager</string> <string name="noti_channel_default">Default Channel</string> <string name="btn_retry_not">Retry</string> <string name="btn_cancel_not">Cancel</string> <string name="file_upload_successful">File has been uploaded successfully</string> <string name="uploading">Uploading</string> <string name="in_progress">in progress</string> <string name="message_failed">File has been not uploaded</string> <string name="message_upload_success">Uploading Success</string> <string name="error_upload_failed">Uploading failed</string> <string name="message_upload_failed">File is not uploaded. Please TRY AGAIN</string> </resources>
2. Add Dependency
In this android app tutorial, we are using for libraries. These are listed below.
- Retrofit – I think no need to much introduction about that. You guys were already aware that one. Retrofit mostly used for calling Remote API
- RxAndroid and RxJava – RxJava and RxJava are most common libraries for these days. They provide react feature in android app development
- Dexter – Manage run time permission in android
- Glide – is image loading libraries that use to show image on the ImageView in android.
Let’s open the app build.gradle file and add some dependencies for using necessary libraries
dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) implementation 'com.android.support:appcompat-v7:28.0.0' implementation 'com.android.support.constraint:constraint-layout:1.1.3' testImplementation 'junit:junit:4.12' androidTestImplementation 'com.android.support.test:runner:1.0.2' androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' implementation 'com.squareup.retrofit2:retrofit:2.4.0' implementation 'com.squareup.retrofit2:converter-gson:2.4.0' // reactive implementation "io.reactivex.rxjava2:rxjava:2.1.10" implementation "io.reactivex.rxjava2:rxandroid:2.0.2" implementation 'com.squareup.retrofit2:adapter-rxjava2:2.4.0' /** * dependency to request the runtime permissions. */ implementation 'com.karumi:dexter:5.0.0' implementation 'com.github.bumptech.glide:glide:4.8.0' annotationProcessor 'com.github.bumptech.glide:compiler:4.8.0' }
3. Let’s Prepare Retrofit instance
Create a new Retrofit Interface and define a method named on file upload. We will use this one for file upload.
3.1 – Interface RestApiService
package com.wave.fileuploadservice.service; import io.reactivex.Single; import okhttp3.MultipartBody; import okhttp3.RequestBody; import okhttp3.ResponseBody; import retrofit2.http.Multipart; import retrofit2.http.POST; import retrofit2.http.Part; /** * Created on : Feb 25, 2019 * Author : AndroidWave */ public interface RestApiService { @Multipart @POST("fileUpload.php") Single<ResponseBody> onFileUpload(@Part("email") RequestBody mEmail, @Part MultipartBody.Part file); }
3.2 – Create Retrofit Service class using RestApiService interface
On above I have created RestApiService interface. Let’s create a service class that return Retrofit instance. We have to add converter factory as well such as RxJava2CallAdapterFactory and GsonConverterFactory
package com.wave.fileuploadservice.service; import retrofit2.Retrofit; import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory; import retrofit2.converter.gson.GsonConverterFactory; import static com.wave.fileuploadservice.BuildConfig.BASE_URL; /** * Created on : Feb 25, 2019 */ public class RetrofitInstance { private static Retrofit retrofit = null; public static RestApiService getApiService() { if (retrofit == null) { retrofit = new Retrofit .Builder() .baseUrl(BASE_URL) .addConverterFactory(GsonConverterFactory.create()) .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) .build(); } return retrofit.create(RestApiService.class); } }
4. Creates a countable RequestBody for listening file progress
package com.wave.fileuploadservice.service; import android.support.annotation.NonNull; import java.io.IOException; import okhttp3.MediaType; import okhttp3.RequestBody; import okio.Buffer; import okio.BufferedSink; import okio.ForwardingSink; import okio.Okio; import okio.Sink; public class CountingRequestBody extends RequestBody { private final RequestBody delegate; private final Listener listener; public CountingRequestBody(RequestBody delegate, Listener listener) { this.delegate = delegate; this.listener = listener; } @Override public MediaType contentType() { return delegate.contentType(); } @Override public long contentLength() { try { return delegate.contentLength(); } catch (IOException e) { e.printStackTrace(); } return -1; } @Override public void writeTo(@NonNull BufferedSink sink) throws IOException { CountingSink countingSink = new CountingSink(sink); BufferedSink bufferedSink = Okio.buffer(countingSink); delegate.writeTo(bufferedSink); bufferedSink.flush(); } final class CountingSink extends ForwardingSink { private long bytesWritten = 0; CountingSink(Sink delegate) { super(delegate); } @Override public void write(@NonNull Buffer source, long byteCount) throws IOException { super.write(source, byteCount); bytesWritten += byteCount; listener.onRequestProgress(bytesWritten, contentLength()); } } public interface Listener { void onRequestProgress(long bytesWritten, long contentLength); } }
5. Now creates a FileUploadService
Create a new subclass of JobIntentService in src folder named is FileUploadService. In this service we are majorly doing three things. let’s check above diagram. We are using two BroadcastReceiver one for listing file upload progress. Second for retry and cancel action button.
package com.wave.fileuploadservice; import android.app.PendingIntent; import android.content.Context; import android.content.Intent; import android.support.annotation.NonNull; import android.support.v4.app.JobIntentService; import android.support.v4.app.NotificationCompat; import android.util.Log; import com.wave.fileuploadservice.receiver.FileProgressReceiver; import com.wave.fileuploadservice.receiver.RetryJobReceiver; import com.wave.fileuploadservice.service.CountingRequestBody; import com.wave.fileuploadservice.service.RestApiService; import com.wave.fileuploadservice.service.RetrofitInstance; import com.wave.fileuploadservice.utils.MIMEType; import io.reactivex.BackpressureStrategy; import io.reactivex.Flowable; import io.reactivex.FlowableEmitter; import io.reactivex.FlowableOnSubscribe; import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.disposables.Disposable; import io.reactivex.functions.Action; import io.reactivex.functions.Consumer; import io.reactivex.schedulers.Schedulers; import java.io.File; import okhttp3.MediaType; import okhttp3.MultipartBody; import okhttp3.RequestBody; import static com.wave.fileuploadservice.receiver.RetryJobReceiver.ACTION_CLEAR; import static com.wave.fileuploadservice.receiver.RetryJobReceiver.ACTION_RETRY; public class FileUploadService extends JobIntentService { private static final String TAG = "FileUploadService"; RestApiService apiService; Disposable mDisposable; public static final int NOTIFICATION_ID = 1; public static final int NOTIFICATION_RETRY_ID = 2; /** * Unique job ID for this service. */ private static final int JOB_ID = 102; String mFilePath; NotificationHelper mNotificationHelper; public static void enqueueWork(Context context, Intent intent) { enqueueWork(context, FileUploadService.class, JOB_ID, intent); } @Override public void onCreate() { super.onCreate(); mNotificationHelper = new NotificationHelper(this); } @Override protected void onHandleWork(@NonNull Intent intent) { Log.d(TAG, "onHandleWork: "); /** * Download/Upload of file * The system or framework is already holding a wake lock for us at this point */ // get file file here mFilePath = intent.getStringExtra("mFilePath"); if (mFilePath == null) { Log.e(TAG, "onHandleWork: Invalid file URI"); return; } apiService = RetrofitInstance.getApiService(); Flowable<Double> fileObservable = Flowable.create(new FlowableOnSubscribe<Double>() { @Override public void subscribe(FlowableEmitter<Double> emitter) throws Exception { apiService.onFileUpload( FileUploadService.this.createRequestBodyFromText("info@androidwave.com"), FileUploadService.this.createMultipartBody(mFilePath, emitter)).blockingGet(); emitter.onComplete(); } }, BackpressureStrategy.LATEST); mDisposable = fileObservable.subscribeOn(Schedulers.computation()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(new Consumer<Double>() { @Override public void accept(Double progress) throws Exception { // call onProgress() FileUploadService.this.onProgress(progress); } }, new Consumer<Throwable>() { @Override public void accept(Throwable throwable) throws Exception { // call onErrors() if error occurred during file upload FileUploadService.this.onErrors(throwable); } }, new Action() { @Override public void run() throws Exception { // call onSuccess() while file upload successful FileUploadService.this.onSuccess(); } }); } private void onErrors(Throwable throwable) { /** * Error occurred in file uploading */ Intent successIntent = new Intent("com.wave.ACTION_CLEAR_NOTIFICATION"); successIntent.putExtra("notificationId", NOTIFICATION_ID); sendBroadcast(successIntent); PendingIntent resultPendingIntent = PendingIntent.getActivity(this, 0 /* Request code */, new Intent(this, MainActivity.class), PendingIntent.FLAG_UPDATE_CURRENT); /** * Add retry action button in notification */ Intent retryIntent = new Intent(this, RetryJobReceiver.class); retryIntent.putExtra("notificationId", NOTIFICATION_RETRY_ID); retryIntent.putExtra("mFilePath", mFilePath); retryIntent.setAction(ACTION_RETRY); /** * Add clear action button in notification */ Intent clearIntent = new Intent(this, RetryJobReceiver.class); clearIntent.putExtra("notificationId", NOTIFICATION_RETRY_ID); clearIntent.putExtra("mFilePath", mFilePath); clearIntent.setAction(ACTION_CLEAR); PendingIntent retryPendingIntent = PendingIntent.getBroadcast(this, 0, retryIntent, 0); PendingIntent clearPendingIntent = PendingIntent.getBroadcast(this, 0, clearIntent, 0); NotificationCompat.Builder mBuilder = mNotificationHelper.getNotification(getString(R.string.error_upload_failed), getString(R.string.message_upload_failed), resultPendingIntent); // attached Retry action in notification mBuilder.addAction(android.R.drawable.ic_menu_revert, getString(R.string.btn_retry_not), retryPendingIntent); // attached Cancel action in notification mBuilder.addAction(android.R.drawable.ic_menu_revert, getString(R.string.btn_cancel_not), clearPendingIntent); // Notify notification mNotificationHelper.notify(NOTIFICATION_RETRY_ID, mBuilder); } /** * Send Broadcast to FileProgressReceiver with progress * * @param progress file uploading progress */ private void onProgress(Double progress) { Intent progressIntent = new Intent(this, FileProgressReceiver.class); progressIntent.setAction("com.wave.ACTION_PROGRESS_NOTIFICATION"); progressIntent.putExtra("notificationId", NOTIFICATION_ID); progressIntent.putExtra("progress", (int) (100 * progress)); sendBroadcast(progressIntent); } /** * Send Broadcast to FileProgressReceiver while file upload successful */ private void onSuccess() { Intent successIntent = new Intent(this, FileProgressReceiver.class); successIntent.setAction("com.wave.ACTION_UPLOADED"); successIntent.putExtra("notificationId", NOTIFICATION_ID); successIntent.putExtra("progress", 100); sendBroadcast(successIntent); } private RequestBody createRequestBodyFromFile(File file, String mimeType) { return RequestBody.create(MediaType.parse(mimeType), file); } private RequestBody createRequestBodyFromText(String mText) { return RequestBody.create(MediaType.parse("text/plain"), mText); } /** * return multi part body in format of FlowableEmitter */ private MultipartBody.Part createMultipartBody(String filePath, FlowableEmitter<Double> emitter) { File file = new File(filePath); return MultipartBody.Part.createFormData("myFile", file.getName(), createCountingRequestBody(file, MIMEType.IMAGE.value, emitter)); } private RequestBody createCountingRequestBody(File file, String mimeType, final FlowableEmitter<Double> emitter) { RequestBody requestBody = createRequestBodyFromFile(file, mimeType); return new CountingRequestBody(requestBody, new CountingRequestBody.Listener() { @Override public void onRequestProgress(long bytesWritten, long contentLength) { double progress = (1.0 * bytesWritten) / contentLength; emitter.onNext(progress); } }); } }
6. Now Create a BroadcastReceiver for listening file upload progress
Create a subclass of BroadcastReceiver named is FileProgressReceiver and override onReceive() methods. As per name suggesting. we receive file upload progress here and update the progress bar notification. Let’s define below action and manages actions accordingly.
package com.wave.fileuploadservice.receiver; import android.app.PendingIntent; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.support.v4.app.NotificationCompat; import com.wave.fileuploadservice.MainActivity; import com.wave.fileuploadservice.NotificationHelper; import com.wave.fileuploadservice.R; import java.util.Objects; public class FileProgressReceiver extends BroadcastReceiver { private static final String TAG = "FileProgressReceiver"; public static final String ACTION_CLEAR_NOTIFICATION = "com.wave.ACTION_CLEAR_NOTIFICATION"; public static final String ACTION_PROGRESS_NOTIFICATION = "com.wave.ACTION_PROGRESS_NOTIFICATION"; public static final String ACTION_UPLOADED = "com.wave.ACTION_UPLOADED"; NotificationHelper mNotificationHelper; public static final int NOTIFICATION_ID = 1; NotificationCompat.Builder notification; @Override public void onReceive(Context mContext, Intent intent) { mNotificationHelper = new NotificationHelper(mContext); // Get notification id int notificationId = intent.getIntExtra("notificationId", 1); // Receive progress int progress = intent.getIntExtra("progress", 0); switch (Objects.requireNonNull(intent.getAction())) { case ACTION_PROGRESS_NOTIFICATION: notification = mNotificationHelper.getNotification(mContext.getString(R.string.uploading), mContext.getString(R.string.in_progress), progress); mNotificationHelper.notify(NOTIFICATION_ID, notification); break; case ACTION_CLEAR_NOTIFICATION: mNotificationHelper.cancelNotification(notificationId); break; case ACTION_UPLOADED: Intent resultIntent = new Intent(mContext, MainActivity.class); PendingIntent resultPendingIntent = PendingIntent.getActivity(mContext, 0 /* Request code */, resultIntent, PendingIntent.FLAG_UPDATE_CURRENT); notification = mNotificationHelper.getNotification(mContext.getString(R.string.message_upload_success), mContext.getString(R.string.file_upload_successful), resultPendingIntent); mNotificationHelper.notify(NOTIFICATION_ID, notification); break; default: break; } } }
7. Create RetryJobReceiver
Create a new class that extends BroadcastReceiver named is RetryJobReceiver. FileUploadService will send a broadcast to RetryJobReceiver in case of an error in during file uploading. Such as network failure, internal server error, etc.
package com.wave.fileuploadservice.receiver; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import com.wave.fileuploadservice.FileUploadService; import com.wave.fileuploadservice.NotificationHelper; import java.util.Objects; public class RetryJobReceiver extends BroadcastReceiver { public static final String ACTION_RETRY = "com.wave.ACTION_RETRY"; public static final String ACTION_CLEAR = "com.wave.ACTION_CLEAR"; NotificationHelper mNotificationHelper; @Override public void onReceive(Context context, Intent intent) { /** * Handle notification user actions */ mNotificationHelper = new NotificationHelper(context); int notificationId = intent.getIntExtra("notificationId", 0); String filePath = intent.getStringExtra("mFilePath"); switch (Objects.requireNonNull(intent.getAction())) { case ACTION_RETRY: mNotificationHelper.cancelNotification(notificationId); Intent mIntent = new Intent(context, FileUploadService.class); mIntent.putExtra("mFilePath", filePath); FileUploadService.enqueueWork(context, mIntent); break; case ACTION_CLEAR: mNotificationHelper.cancelNotification(notificationId); break; default: break; } } }
8. Declare JobIntentService, Permission, and Receiver in AndroidManifest
8.1 Permission for storage, camera and Internet permission
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.CAMERA" /> <uses-permission android:name="android.permission.INTERNET" /> <uses-feature android:name="android.hardware.camera" /> <uses-feature android:name="android.hardware.camera.autofocus" /> <uses-feature android:name="android.hardware.camera.flash" />
8.2 Declare JobIntentService and related permission
For pre oreo devices declare WAKE_LOCK permission
<!--for JobIntentService--> <uses-permission android:name="android.permission.WAKE_LOCK" />
Inside the application tag declared FileUploadService
<service android:name=".FileUploadService" android:permission="android.permission.BIND_JOB_SERVICE" />
For Receiver
<receiver android:name=".receiver.FileProgressReceiver"> <intent-filter> <action android:name="com.wave.ACTION_CLEAR_NOTIFICATION" /> <action android:name="com.wave.ACTION_PROGRESS_NOTIFICATION" /> <action android:name="com.wave.ACTION_UPLOADED" /> </intent-filter> </receiver> <receiver android:name=".receiver.RetryJobReceiver"> <intent-filter> <action android:name="com.wave.ACTION_RETRY" /> <action android:name="com.wave.ACTION_CLEAR" /> </intent-filter> </receiver>
9. Create a File Provider for getting file from storage
Create resource file inside the res=>xml=>file_provider_paths.xml and add below code
<?xml version="1.0" encoding="utf-8"?> <paths> <external-path name="my_images" path="Android/data/com.wave.fileuploadservice/files/Pictures" /> <!-- replace com.wave.fileuploadservice with your package name --> </paths>
10. Define provider inside AndroidManifest.xml
<provider android:name="android.support.v4.content.FileProvider" android:authorities="${applicationId}.provider" android:exported="false" android:grantUriPermissions="true"> <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/file_provider_paths" /> </provider>
11. The complete AndroidManifest.xml looks like
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.wave.fileuploadservice"> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.CAMERA" /> <uses-permission android:name="android.permission.INTERNET" /> <uses-feature android:name="android.hardware.camera" /> <uses-feature android:name="android.hardware.camera.autofocus" /> <uses-feature android:name="android.hardware.camera.flash" /> <!--for JobIntentService--> <uses-permission android:name="android.permission.WAKE_LOCK" /> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/AppTheme"> <activity android:name=".MainActivity"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <provider android:name="android.support.v4.content.FileProvider" android:authorities="${applicationId}.provider" android:exported="false" android:grantUriPermissions="true"> <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/file_provider_paths" /> </provider> <service android:name=".FileUploadService" android:permission="android.permission.BIND_JOB_SERVICE" /> <receiver android:name=".receiver.FileProgressReceiver"> <intent-filter> <action android:name="com.wave.ACTION_CLEAR_NOTIFICATION" /> <action android:name="com.wave.ACTION_PROGRESS_NOTIFICATION" /> <action android:name="com.wave.ACTION_UPLOADED" /> </intent-filter> </receiver> <receiver android:name=".receiver.RetryJobReceiver"> <intent-filter> <action android:name="com.wave.ACTION_RETRY" /> <action android:name="com.wave.ACTION_CLEAR" /> </intent-filter> </receiver> </application> </manifest>
12. Create a Helper class for manage notification named is NotificationHelper
package com.wave.fileuploadservice; import android.app.Notification; import android.app.NotificationChannel; import android.app.NotificationManager; import android.app.PendingIntent; import android.content.Context; import android.content.ContextWrapper; import android.graphics.Color; import android.support.v4.app.NotificationCompat; import android.support.v4.content.ContextCompat; /** * Helper class to manage notification channels, and create notifications. */ public class NotificationHelper extends ContextWrapper { private NotificationManager manager; public static final String WAVE_CHANNEL = "default"; public NotificationHelper(Context mContext) { super(mContext); NotificationChannel mChannel = null; if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) { mChannel = new NotificationChannel(WAVE_CHANNEL, getString(R.string.noti_channel_default), NotificationManager.IMPORTANCE_DEFAULT); mChannel.setLightColor(Color.GREEN); mChannel.setLockscreenVisibility(Notification.VISIBILITY_PRIVATE); getManager().createNotificationChannel(mChannel); } } public NotificationCompat.Builder getNotification(String title, String body, int progress) { NotificationCompat.Builder mBuilder; mBuilder = new NotificationCompat.Builder(getApplicationContext(), WAVE_CHANNEL); mBuilder.setSmallIcon(getSmallIcon()); mBuilder.setColor(ContextCompat.getColor(getApplicationContext(), R.color.colorAccent)); mBuilder.setContentTitle(title) .setContentText(body) .setOngoing(true) //.setContentIntent(resultPendingIntent) .setDefaults(NotificationCompat.DEFAULT_ALL) .setPriority(NotificationCompat.PRIORITY_HIGH); mBuilder.setVibrate(new long[] { 0L }); mBuilder.setVisibility(NotificationCompat.VISIBILITY_PUBLIC); mBuilder.setProgress(100, progress, false); if (progress == 100) { mBuilder.setProgress(0, 0, false); mBuilder.setContentText(body); } return mBuilder; } public NotificationCompat.Builder getNotification(String title, String body, PendingIntent resultPendingIntent) { NotificationCompat.Builder mBuilder; mBuilder = new NotificationCompat.Builder(getApplicationContext(), WAVE_CHANNEL); mBuilder.setSmallIcon(getSmallIcon()); mBuilder.setColor(ContextCompat.getColor(getApplicationContext(), R.color.colorAccent)); mBuilder.setContentTitle(title) .setContentText(body) .setContentIntent(resultPendingIntent) .setDefaults(NotificationCompat.DEFAULT_ALL) .setPriority(NotificationCompat.PRIORITY_HIGH); mBuilder.setVibrate(new long[] { 0L }); mBuilder.setVisibility(NotificationCompat.VISIBILITY_PUBLIC); return mBuilder; } /** * Send a notification. * * @param id The ID of the notification * @param notification The notification object */ public void notify(int id, NotificationCompat.Builder notification) { getManager().notify(id, notification.build()); } /** * Get the small icon for this app * * @return The small icon resource id */ private int getSmallIcon() { return android.R.drawable.stat_notify_sync; } /** * Get the notification manager. * <p> * Utility method as this helper works with it a lot. * * @return The system service NotificationManager */ private NotificationManager getManager() { if (manager == null) { manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); } return manager; } public void cancelNotification(int notificationId) { getManager().cancel(notificationId); } }
13. Go in the Activity and do following operation
- Open activity xml and add some components such as ImageView for displaying selected and captured image, button for requesting camera and gallery intent. and One button for starting JobIntentService.
- Get an image from gallery and camera
- Enqueue the Job and pass file path with Intent.
14 . The Complete code of MainActivity
package com.wave.fileuploadservice; import android.Manifest; import android.content.DialogInterface; import android.content.Intent; import android.database.Cursor; import android.net.Uri; import android.os.Bundle; import android.os.Environment; import android.provider.MediaStore; import android.support.v4.content.FileProvider; import android.support.v7.app.AlertDialog; import android.support.v7.app.AppCompatActivity; import android.view.View; import android.widget.Button; import android.widget.ImageView; import android.widget.TextView; import android.widget.Toast; 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.DexterError; import com.karumi.dexter.listener.PermissionRequest; import com.karumi.dexter.listener.PermissionRequestErrorListener; import com.karumi.dexter.listener.multi.MultiplePermissionsListener; import java.io.File; import java.io.IOException; import java.util.Calendar; import java.util.List; public class MainActivity extends AppCompatActivity implements View.OnClickListener { static final int REQUEST_TAKE_PHOTO = 101; static final int REQUEST_GALLERY_PHOTO = 102; File mPhotoFile; ImageView ivDisplayImage; Button buttonUpload; TextView tvSelectedFilePath; ImageView ivSelectImage; TextView txvResult; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ivDisplayImage = findViewById(R.id.ivDisplayImage); buttonUpload = findViewById(R.id.buttonUpload); tvSelectedFilePath = findViewById(R.id.tvSelectedFilePath); ivSelectImage = findViewById(R.id.imageView2); txvResult = findViewById(R.id.tvResult); buttonUpload.setOnClickListener(this); ivSelectImage.setOnClickListener(this); } @Override public void onClick(View v) { switch (v.getId()) { case R.id.buttonUpload: if (tvSelectedFilePath.getText().toString().isEmpty()) { Toast.makeText(this, "Select file first", Toast.LENGTH_LONG).show(); return; } Intent mIntent = new Intent(this, FileUploadService.class); mIntent.putExtra("mFilePath", tvSelectedFilePath.getText().toString()); FileUploadService.enqueueWork(this, mIntent); break; case R.id.imageView2: selectImage(); break; } } /** * Alert dialog for capture or select from galley */ private void selectImage() { final CharSequence[] items = { "Take Photo", "Choose from Library", "Cancel" }; AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this); builder.setItems(items, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int item) { if (items[item].equals("Take Photo")) { MainActivity.this.requestStoragePermission(true); } else if (items[item].equals("Choose from Library")) { MainActivity.this.requestStoragePermission(false); } else if (items[item].equals("Cancel")) { dialog.dismiss(); } } }); builder.show(); } /** * Requesting multiple permissions (storage and camera) at once * This uses multiple permission model from dexter * On permanent denial opens settings dialog */ private void requestStoragePermission(final boolean isCamera) { Dexter.withActivity(this) .withPermissions(Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.CAMERA) .withListener(new MultiplePermissionsListener() { @Override public void onPermissionsChecked(MultiplePermissionsReport report) { // check if all permissions are granted if (report.areAllPermissionsGranted()) { if (isCamera) { startCamera(); } else { chooseGallery(); } } // check for permanent denial of any permission if (report.isAnyPermissionPermanentlyDenied()) { // show alert dialog navigating to Settings chooseGallery(); } } @Override public void onPermissionRationaleShouldBeShown(List<PermissionRequest> permissions, PermissionToken token) { token.continuePermissionRequest(); } }) .withErrorListener(new PermissionRequestErrorListener() { @Override public void onError(DexterError error) { Toast.makeText(MainActivity.this.getApplicationContext(), "Error occurred! ", Toast.LENGTH_SHORT).show(); } }) .onSameThread() .check(); } public void startCamera() { mPhotoFile = newFile(); Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); if (takePictureIntent.resolveActivity(getPackageManager()) != null) { if (mPhotoFile != null) { Uri photoURI = FileProvider.getUriForFile(this, BuildConfig.APPLICATION_ID + ".provider", mPhotoFile); takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI); startActivityForResult(takePictureIntent, REQUEST_TAKE_PHOTO); } } } 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, Intent data) { if (resultCode == RESULT_OK) { if (requestCode == REQUEST_TAKE_PHOTO) { tvSelectedFilePath.setText(mPhotoFile.getAbsolutePath()); Glide.with(MainActivity.this) .load(mPhotoFile) .apply(new RequestOptions().centerCrop().circleCrop()) .into(ivDisplayImage); } else if (requestCode == REQUEST_GALLERY_PHOTO) { Uri selectedImage = data.getData(); tvSelectedFilePath.setText(getRealPathFromUri(selectedImage)); Glide.with(MainActivity.this) .load(getRealPathFromUri(selectedImage)) .apply(new RequestOptions().centerCrop().circleCrop()) .into(ivDisplayImage); } } } public File newFile() { Calendar cal = Calendar.getInstance(); long timeInMillis = cal.getTimeInMillis(); String mFileName = String.valueOf(timeInMillis) + ".jpeg"; File mFilePath = getFilePath(); try { File newFile = new File(mFilePath.getAbsolutePath(), mFileName); newFile.createNewFile(); return newFile; } catch (IOException e) { e.printStackTrace(); } return null; } public File getFilePath() { return getExternalFilesDir(Environment.DIRECTORY_PICTURES); } public String getRealPathFromUri(Uri contentUri) { Cursor cursor = null; try { String[] proj = { MediaStore.Images.Media.DATA }; cursor = getContentResolver().query(contentUri, proj, null, null, null); assert cursor != null; int columnIndex = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA); cursor.moveToFirst(); return cursor.getString(columnIndex); } finally { if (cursor != null) { cursor.close(); } } } }
After following all the above step your app is ready to use. I hope it’s helpful for you, then help me by sharing this post with all your friends who learning android app development.
If you have any queries, feel free to ask them in the comment section below. Happy Coding
10 Comments
hi thank you for your tutorial.
can you show me how to get API JSON response from this tutorial
dear, first very thank you for your great effort in developing this tutorial, second am ask you if there is any tutorial about download files from the server and pause/resume the download using the same scenario you followed here
como recibo la imagen en fileUpload.php
what is the code fileUpload.php
Hi, this example not working when app killed while uploading multiple files..
Hi, it’s a nice example. I’ve integrated it and it’s work without any error. But my image is not uploading anywhere..! not on the server and not on the device, it’s only showing ‘File successfully uploaded’ in Result
I’ve set server path in base_url and package path in file_provider_path.xml
where I’m wrong?
I getting this exception
Failed resolution of: Landroid/app/NotificationChannel;
Managed to fix it by moving this line code into if statement.
NotificationChannel mChannel = null;
Very nice tutorial. Do you have example on retrofit2(coroutine) ?
IS ok,solved.