Load image from url in notification Android
AndroidBitmapGoogle Cloud-MessagingAndroid NotificationsAndroid Notification-BarAndroid Problem Overview
In my android application, i want to set Notification icons dynamically which will be loaded from URL. For that, i have used setLargeIcon
property of NotificationBuilder in receiver
.I reffered many link and tried various solutions but couldn't get desired output. Though i downloaded that image from url and setting that bitmap in notification, it is not being displayed. Instead it displays the setSmallIcon
image as large icon. I don't know where i am going wrong. Here i am posting my code. Please help me to solve this issue. Thank you.
Code:
@SuppressLint("NewApi")
public class C2DMMessageReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if ("com.google.android.c2dm.intent.RECEIVE".equals(action)) {
Log.e("C2DM", "received message");
final String fullName = intent.getStringExtra("message");
final String payload1 = intent.getStringExtra("message1");
final String payload2 = intent.getStringExtra("message2");
final String userImage = intent.getStringExtra("userImage");
Log.e("userImage Url :", userImage); //it shows correct url
new sendNotification(context)
.execute(fullName, payload1, userImage);
}
}
private class sendNotification extends AsyncTask<String, Void, Bitmap> {
Context ctx;
String message;
public sendNotification(Context context) {
super();
this.ctx = context;
}
@Override
protected Bitmap doInBackground(String... params) {
InputStream in;
message = params[0] + params[1];
try {
in = new URL(params[2]).openStream();
Bitmap bmp = BitmapFactory.decodeStream(in);
return bmp;
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
@Override
protected void onPostExecute(Bitmap result) {
super.onPostExecute(result);
try {
NotificationManager notificationManager = (NotificationManager) ctx
.getSystemService(Context.NOTIFICATION_SERVICE);
Intent intent = new Intent(ctx, NotificationsActivity.class);
intent.putExtra("isFromBadge", false);
Notification notification = new Notification.Builder(ctx)
.setContentTitle(
ctx.getResources().getString(R.string.app_name))
.setContentText(message)
.setSmallIcon(R.drawable.ic_launcher)
.setLargeIcon(result).build();
// hide the notification after its selected
notification.flags |= Notification.FLAG_AUTO_CANCEL;
notificationManager.notify(1, notification);
} catch (Exception e) {
e.printStackTrace();
}
}
}
Android Solutions
Solution 1 - Android
Changed my code as below and its working now :
private class sendNotification extends AsyncTask<String, Void, Bitmap> {
Context ctx;
String message;
public sendNotification(Context context) {
super();
this.ctx = context;
}
@Override
protected Bitmap doInBackground(String... params) {
InputStream in;
message = params[0] + params[1];
try {
URL url = new URL(params[2]);
HttpURLConnection connection = (HttpURLConnection)url.openConnection();
connection.setDoInput(true);
connection.connect();
in = connection.getInputStream();
Bitmap myBitmap = BitmapFactory.decodeStream(in);
return myBitmap;
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
@Override
protected void onPostExecute(Bitmap result) {
super.onPostExecute(result);
try {
NotificationManager notificationManager = (NotificationManager) ctx
.getSystemService(Context.NOTIFICATION_SERVICE);
Intent intent = new Intent(ctx, NotificationsActivity.class);
intent.putExtra("isFromBadge", false);
Notification notification = new Notification.Builder(ctx)
.setContentTitle(
ctx.getResources().getString(R.string.app_name))
.setContentText(message)
.setSmallIcon(R.drawable.ic_launcher)
.setLargeIcon(result).build();
// hide the notification after its selected
notification.flags |= Notification.FLAG_AUTO_CANCEL;
notificationManager.notify(1, notification);
} catch (Exception e) {
e.printStackTrace();
}
}
}
Solution 2 - Android
How to implement BigPicture style Notification:
Miracle has been done by .setStyle(new Notification.BigPictureStyle().bigPicture(result))
:
I have done this way with:
Generate notification by AsyncTask:
new generatePictureStyleNotification(this,"Title", "Message",
"http://api.androidhive.info/images/sample.jpg").execute();
AsyncTask:
public class generatePictureStyleNotification extends AsyncTask<String, Void, Bitmap> {
private Context mContext;
private String title, message, imageUrl;
public generatePictureStyleNotification(Context context, String title, String message, String imageUrl) {
super();
this.mContext = context;
this.title = title;
this.message = message;
this.imageUrl = imageUrl;
}
@Override
protected Bitmap doInBackground(String... params) {
InputStream in;
try {
URL url = new URL(this.imageUrl);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setDoInput(true);
connection.connect();
in = connection.getInputStream();
Bitmap myBitmap = BitmapFactory.decodeStream(in);
return myBitmap;
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
@Override
protected void onPostExecute(Bitmap result) {
super.onPostExecute(result);
Intent intent = new Intent(mContext, MyOpenableActivity.class);
intent.putExtra("key", "value");
PendingIntent pendingIntent = PendingIntent.getActivity(mContext, 100, intent, PendingIntent.FLAG_ONE_SHOT);
NotificationManager notificationManager = (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
Notification notif = new Notification.Builder(mContext)
.setContentIntent(pendingIntent)
.setContentTitle(title)
.setContentText(message)
.setSmallIcon(R.mipmap.ic_launcher)
.setLargeIcon(result)
.setStyle(new Notification.BigPictureStyle().bigPicture(result))
.build();
notif.flags |= Notification.FLAG_AUTO_CANCEL;
notificationManager.notify(1, notif);
}
}
Solution 3 - Android
you can do this using Glide like this:
val notificationBuilder = NotificationCompat.Builder(this, channelId)
.setSmallIcon(R.drawable.ic_message)
.setContentTitle("title")
.setContentText("text")
val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
val futureTarget = Glide.with(this)
.asBitmap()
.load(photoUrl)
.submit()
val bitmap = futureTarget.get()
notificationBuilder.setLargeIcon(bitmap)
Glide.with(this).clear(futureTarget)
notificationManager.notify(0, notificationBuilder.build())
Solution 4 - Android
Top answer in Kotlin and with Coroutines. This method applies the bitmap to the builder
instead of a direct assignment, and of course if bitmap available. It's nice because if the url is wrong it will be caught in the try/catch.
fun applyImageUrl(
builder: NotificationCompat.Builder,
imageUrl: String
) = runBlocking {
val url = URL(imageUrl)
withContext(Dispatchers.IO) {
try {
val input = url.openStream()
BitmapFactory.decodeStream(input)
} catch (e: IOException) {
null
}
}?.let { bitmap ->
builder.setLargeIcon(bitmap)
}
}
with Kotlin & RxJava
fun applyImageUrl(
builder: NotificationCompat.Builder,
imageUrl: String
) {
val url = URL(imageUrl)
Single.create<Bitmap> { emitter ->
try {
val input = url.openStream()
val bitmap = BitmapFactory.decodeStream(input)
emitter.onSuccess(bitmap)
} catch (e: Exception) {
emitter.onError(e)
}
}.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{
builder.setLargeIcon(it)
}, {
Timber.e("error generating bitmap for notification")
}
)
}
Solution 5 - Android
Since image is loaded from internet, it should be done async in a background thread. Either use async task or Glide (for efficient image loading).
To load image notification, you need to use "NotificationCompat.BigPictureStyle()". This requires a bitmap (which has to be extracted from image url)
Most of the API's and methods of Glide are now deprecated. Below is working with Glide 4.9 and upto Android 10.
// Load bitmap from image url on background thread and display image notification
private void getBitmapAsyncAndDoWork(String imageUrl) {
final Bitmap[] bitmap = {null};
Glide.with(getApplicationContext())
.asBitmap()
.load(imageUrl)
.into(new CustomTarget<Bitmap>() {
@Override
public void onResourceReady(@NonNull Bitmap resource, @Nullable Transition<? super Bitmap> transition) {
bitmap[0] = resource;
// TODO Do some work: pass this bitmap
displayImageNotification(bitmap[0]);
}
@Override
public void onLoadCleared(@Nullable Drawable placeholder) {
}
});
}
Display the image notification once, the bitmap is ready.
private void displayImageNotification(Bitmap bitmap) {
NotificationCompat.Builder builder = new NotificationCompat.Builder(getApplicationContext(), getChannelId());
builder
.setContentTitle(title)
.setContentText(subtext)
.setDefaults(Notification.DEFAULT_LIGHTS | Notification.DEFAULT_VIBRATE)
.setSmallIcon(SMALL_ICON)
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
.setColor(getApplicationContext().getColor(color))
.setAutoCancel(true)
.setOngoing(false)
.setOnlyAlertOnce(true)
.setContentIntent(pendingIntent)
.setStyle(
new NotificationCompat.BigPictureStyle().bigPicture(bitmap))
.setPriority(Notification.PRIORITY_HIGH);
getManager().notify(tag, id, builder.build());
}
Solution 6 - Android
I know a good answer has been given so let's see if we can make it easier to understand and implement.
---------------------Theory------------------------
The problem can be abstracted in two step solution namely
-
Get image from a URL
-
Decode image and pass to notification builder
-
Get image from a URL
InputStream in = new URL("Img URL goes here eg. http://gg.com/profile.jpg").openStream();
-
Decode and pass to notification
Bitmap bmp = null;
#create a null bmp container that will be used to hold decoded img
bmp = BitmapFactory.decodeStream(in);
# save the image into container
Voila!
once you build the image and save it in the variable bmp, you can call it on notification builder
.setLargeIcon(bmp)
--------Implementation---------------
Android studio will encourage you to wrap your code with try catch so the end product will look like this.
Bitmap bmp = null;
try {
InputStream in = new URL("url goes here").openStream();
bmp = BitmapFactory.decodeStream(in);
} catch (IOException e) {
e.printStackTrace();
}
Once you have the bmp you can call it in notification builder as
NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(this, CHANNEL_ID)
.setSmallIcon(R.drawable.ic_launcher)
.setContentText("title")
.setContentText("text goes here")
.setLargeIcon(bmp)
.setAutoCancel(true);
Solution 7 - Android
Using Picasso Library.
Target target = new Target() {
@Override
public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
largeIcon=bitmap;
}
@Override
public void onBitmapFailed(Drawable errorDrawable) {
}
@Override
public void onPrepareLoad(Drawable placeHolderDrawable) {
}
};
Picasso.with(this).load("url").into(target);
NotificationCompat.Builder notificationBuilder =
new NotificationCompat.Builder(this, channelId)
.setSmallIcon(R.drawable.icon)
.setContentTitle(msg.getString("title"))
.setContentText(msg.getString("msg"))
.setAutoCancel(true)
.setSound(defaultSoundUri)
.setLargeIcon(largeIcon)
.setContentIntent(pendingIntent);
Solution 8 - Android
Since I couldn't find any working solution for Picasso I'm posting my complete and working(July 2020) example using Picasso below.
It is sending the notification immediately and then updates it when the image for setLargeIcon() has been loaded. Normally this is very quick and the user should only see the updated version of the notification in most cases.
private void sendNotification(String message, String title, final String photoUrl) {
Intent intent = new Intent(this, MainActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent,
PendingIntent.FLAG_ONE_SHOT);
final NotificationCompat.Builder notificationBuilder =
new NotificationCompat.Builder(this, CHANNEL_ID)
.setSmallIcon(R.drawable.wbib_transp_512)
.setContentTitle(title)
.setContentText(message)
.setAutoCancel(true)
.setPriority(NotificationCompat.PRIORITY_HIGH)
.setCategory(NotificationCompat.CATEGORY_MESSAGE)
.setContentIntent(pendingIntent);
final NotificationManager notificationManager =
(NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(0, notificationBuilder.build());
final Handler uiHandler = new Handler(Looper.getMainLooper());
uiHandler.post(new Runnable() {
@Override
public void run() {
Picasso.get()
.load(photoUrl)
.resize(200, 200)
.into(new Target() {
@Override
public void onBitmapLoaded(final Bitmap bitmap, final Picasso.LoadedFrom from) {
notificationBuilder.setLargeIcon(bitmap);
notificationManager.notify(0, notificationBuilder.build());
}
@Override
public void onBitmapFailed(Exception e, final Drawable errorDrawable) {
// Do nothing?
}
@Override
public void onPrepareLoad(final Drawable placeHolderDrawable) {
// Do nothing?
}
});
}
});
}
Solution 9 - Android
RxJava and Picasso way
private fun bigImageNotification(ctx: Context, title: String, msg: String, imgUrl: String): Disposable? {
return Observable.fromCallable {
Picasso.get().load(imgUrl).get()
}
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe({
createNotification(ctx, title, msg, it)
}, {it.printStackTrace()})
}
private fun createNotification(ctx: Context, title: String, msg: String, img: Bitmap?) {
val b = Notification.Builder(ctx)
b.setAutoCancel(true)
.setSmallIcon(R.mipmap.ic_launcher)
.setContentTitle(title)
.setContentText(msg)
.setStyle(Notification.BigPictureStyle().bigPicture(img))
val notificationManager = ctx.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
notificationManager.notify(1000, b.build())
}
Usage
bigImageNotification(context, "title", "msg", "https://www.google.com/images/branding/googlelogo/1x/googlelogo_color_272x92dp.png")