In this tutorial, we’ll be using the Retrofit library in order to download an image from a URL. We will show the progress of the download in the notification task. We’ll be using an image from unsplash.com.
Android Retrofit Download Image
In our previous tutorial, we built an android application which we download a file using Retrofit and show the progress in the ProgressBar.
In order to build the app which downloads in the background and shows and updates the download progress in the Notification we need to do the following things:
- Create an Intent Service.
- Handle the download logic and notification creations inside it.
- Create a Broadcast Receiver in our Activity to listen to updates from the Intent Service.
- Once the download is complete, save the file to the disk.
- Retrieve the File(image in this case) in your Activity.
- Use Picasso to display it onto the Screen.
Project Setup
Add the following dependencies in your app’s build.gradle file:
1 2 3 4 5 |
implementation 'com.android.support:design:28.0.0-alpha3' implementation 'com.squareup.retrofit2:retrofit:2.3.0' implementation 'com.squareup.picasso:picasso:2.71828' |
Add the following permissions in your AndroidManifest.xml file
1 2 3 4 |
<uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> |
Android Retrofit Download Image Progress in Notification Project Structure
Code
The code for the activity_main.xml layout is given below:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="https://schemas.android.com/apk/res/android" xmlns:app="https://schemas.android.com/apk/res-auto" xmlns:tools="https://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_margin="@android:dimen/app_icon_size" tools:context=".MainActivity"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentTop="true" android:gravity="center" android:text="Download Image With Progress In Notifications Using Retrofit" /> <ImageView android:id="@+id/imageView" android:layout_width="150dp" android:layout_height="150dp" android:layout_centerInParent="true" app:srcCompat="@mipmap/ic_launcher" /> <android.support.design.widget.FloatingActionButton android:id="@+id/fab" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="@+id/imageView" android:layout_centerHorizontal="true" android:layout_margin="16dp" android:src="@android:drawable/stat_sys_download" /> </RelativeLayout> |
The code for the RetrofitInterface.java
is given below.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
package com.journaldev.androidretrofitprogressnotification; import okhttp3.ResponseBody; import retrofit2.Call; import retrofit2.http.GET; import retrofit2.http.Streaming; import retrofit2.http.Url; public interface RetrofitInterface { @GET() @Streaming Call<ResponseBody> downloadImage(@Url String fileUrl); } |
The code for the MainActivity.java class is given below:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 |
package com.journaldev.androidretrofitprogressnotification; import android.Manifest; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.PackageManager; import android.os.Environment; import android.support.design.widget.FloatingActionButton; import android.support.v4.app.ActivityCompat; import android.support.v4.content.ContextCompat; import android.support.v4.content.LocalBroadcastManager; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.view.View; import android.widget.ImageView; import android.widget.Toast; import com.squareup.picasso.Picasso; import java.io.File; public class MainActivity extends AppCompatActivity { public static final String PROGRESS_UPDATE = "progress_update"; private static final int PERMISSION_REQUEST_CODE = 1; ImageView imageView; FloatingActionButton fab; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); imageView = findViewById(R.id.imageView); fab = findViewById(R.id.fab); fab.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { if (checkPermission()) { startImageDownload(); } else { requestPermission(); } } }); registerReceiver(); } private void registerReceiver() { LocalBroadcastManager bManager = LocalBroadcastManager.getInstance(this); IntentFilter intentFilter = new IntentFilter(); intentFilter.addAction(PROGRESS_UPDATE); bManager.registerReceiver(mBroadcastReceiver, intentFilter); } private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { if (intent.getAction().equals(PROGRESS_UPDATE)) { boolean downloadComplete = intent.getBooleanExtra("downloadComplete", false); //Log.d("API123", download.getProgress() + " current progress"); if (downloadComplete) { Toast.makeText(getApplicationContext(), "File download completed", Toast.LENGTH_SHORT).show(); File file = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).getPath() + File.separator + "journaldev-image-downloaded.jpg"); Picasso.get().load(file).into(imageView); } } } }; private boolean checkPermission() { int result = ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE); return result == PackageManager.PERMISSION_GRANTED; } private void startImageDownload() { Intent intent = new Intent(this, BackgroundNotificationService.class); startService(intent); } private void requestPermission() { ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, PERMISSION_REQUEST_CODE); } @Override public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) { switch (requestCode) { case PERMISSION_REQUEST_CODE: if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { startImageDownload(); } else { Toast.makeText(getApplicationContext(), "Permission Denied", Toast.LENGTH_SHORT).show(); } break; } } } |
In the above code, we start the BackgroundNotificationService.java
IntentService when the FloatingActionButton is clicked.
Before that, we check for the Runtime Permissions.
We’ve also created and registered an implicit BroadcastReceiver.
When the download would get completed it would send a message to the BroadcastReceiver via Intents.
Once the download is successfully completed we retrieve the saved file from the Internal Storage and display it in the ImageView using Picasso.
Let’s look at the BackgroundNotificationService.java
class where all the download stuff is happening:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 |
package com.journaldev.androidretrofitprogressnotification; import android.app.IntentService; import android.app.NotificationChannel; import android.app.NotificationManager; import android.content.Context; import android.content.Intent; import android.graphics.Color; import android.os.Build; import android.os.Environment; import android.support.v4.app.NotificationCompat; import android.support.v4.content.LocalBroadcastManager; import android.widget.Toast; import java.io.BufferedInputStream; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import okhttp3.ResponseBody; import retrofit2.Call; import retrofit2.Retrofit; public class BackgroundNotificationService extends IntentService { public BackgroundNotificationService() { super("Service"); } private NotificationCompat.Builder notificationBuilder; private NotificationManager notificationManager; @Override protected void onHandleIntent(Intent intent) { notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { NotificationChannel notificationChannel = new NotificationChannel("id", "an", NotificationManager.IMPORTANCE_LOW); notificationChannel.setDescription("no sound"); notificationChannel.setSound(null, null); notificationChannel.enableLights(false); notificationChannel.setLightColor(Color.BLUE); notificationChannel.enableVibration(false); notificationManager.createNotificationChannel(notificationChannel); } notificationBuilder = new NotificationCompat.Builder(this, "id") .setSmallIcon(android.R.drawable.stat_sys_download) .setContentTitle("Download") .setContentText("Downloading Image") .setDefaults(0) .setAutoCancel(true); notificationManager.notify(0, notificationBuilder.build()); initRetrofit(); } private void initRetrofit() { Retrofit retrofit = new Retrofit.Builder() .baseUrl("https://unsplash.com/") .build(); RetrofitInterface retrofitInterface = retrofit.create(RetrofitInterface.class); Call<ResponseBody> request = retrofitInterface.downloadImage("photos/YYW9shdLIwo/download?force=true"); try { downloadImage(request.execute().body()); } catch (IOException e) { e.printStackTrace(); Toast.makeText(getApplicationContext(), e.getMessage(), Toast.LENGTH_SHORT).show(); } } private void downloadImage(ResponseBody body) throws IOException { int count; byte data[] = new byte[1024 * 4]; long fileSize = body.contentLength(); InputStream inputStream = new BufferedInputStream(body.byteStream(), 1024 * 8); File outputFile = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS), "journaldev-image-downloaded.jpg"); OutputStream outputStream = new FileOutputStream(outputFile); long total = 0; boolean downloadComplete = false; //int totalFileSize = (int) (fileSize / (Math.pow(1024, 2))); while ((count = inputStream.read(data)) != -1) { total += count; int progress = (int) ((double) (total * 100) / (double) fileSize); updateNotification(progress); outputStream.write(data, 0, count); downloadComplete = true; } onDownloadComplete(downloadComplete); outputStream.flush(); outputStream.close(); inputStream.close(); } private void updateNotification(int currentProgress) { notificationBuilder.setProgress(100, currentProgress, false); notificationBuilder.setContentText("Downloaded: " + currentProgress + "%"); notificationManager.notify(0, notificationBuilder.build()); } private void sendProgressUpdate(boolean downloadComplete) { Intent intent = new Intent(MainActivity.PROGRESS_UPDATE); intent.putExtra("downloadComplete", downloadComplete); LocalBroadcastManager.getInstance(BackgroundNotificationService.this).sendBroadcast(intent); } private void onDownloadComplete(boolean downloadComplete) { sendProgressUpdate(downloadComplete); notificationManager.cancel(0); notificationBuilder.setProgress(0, 0, false); notificationBuilder.setContentText("Image Download Complete"); notificationManager.notify(0, notificationBuilder.build()); } @Override public void onTaskRemoved(Intent rootIntent) { notificationManager.cancel(0); } } |
Inside the onHandleIntent
we create the Notification first and then Retrofit instance.
Inside the Retrofit call, we do the download stuff and update the progress on the Notification.
To show a ProgressBar inside the Notification you just need to call setProgress on the Notification Builder instance.
sendProgressUpdate
sends the update to the Broadcast Receiver.
The output of the above application in action is given below:
This brings an end to this tutorial. You can download the final project from the link below: