Load image from URL in notification Android
L

9

73

In my Android application, I want to set Notification icons dynamically which will be loaded from URL. For that, I have used the setLargeIcon property of NotificationBuilder in receiver.

I referred to many links and tried various solutions, but I couldn't get the desired output. Though I downloaded that image from the URL and setting that bitmap in the notification, it is not being displayed. Instead it displays the setSmallIcon image as a large icon. I don't know where I am going wrong. Here I am posting my code. How can I solve this issue?

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();
            }
        }
    }
Lassalle answered 19/7, 2014 at 12:15 Comment(3)
Can you post a screenshot?Amoretto
Its not possible for me to attach screenshot but i can tell you that how it looks. Actually it should display smallIcon value(R.drawable.ic_launcher) in notificationbar and when i enlarge my notification bar by swiping downside, it should dispaly bitmap(bm) which is set as large icon but in both notifictaionbar and enlarged notifictaion area shows small icon (R.drawable.ic_launcher).Lassalle
possible duplicate of Android - use external profile image in notification bar like FacebookPlatitude
L
47

I changed my code as below, and it’s 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();
            }
        }
    }
Lassalle answered 21/7, 2014 at 13:16 Comment(2)
how to handle image size ??Bindweed
see my answer in 2019 implementing this answer using Coroutines and Kotlin :)Self
S
40

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())

Preview

Sleeve answered 21/12, 2018 at 10:7 Comment(1)
The problem is that once the bitmap is finished downloading, it won't magically appear on the notification, it won't auto refresh. One has to implement callback that would re-generate the notification. I tried refreshing it on "onResourceReady", but it didn't work, so won't go deeper into this rabbit hole.Fumikofumitory
M
38

How to implement a BigPicture style notification:

A miracle has been done by .setStyle(new Notification.BigPictureStyle().bigPicture(result)):

I have done it this way with:

Enter image description here

Generate a 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);
        }
    }
Miele answered 7/12, 2015 at 7:12 Comment(10)
Put some details along with code! It would help someone to understand the APIs, functionality and the implantation exactly!Koerlin
i am not able to call this method in FirebaseMessagingService How can i implement it.Sufficient
@ArpitPatel May be the service is ending prior to retrieving image asynchronously. Did you try getting the image synchronously? Like here: androidbash.com/firebase-push-notification-androidSunil
ya i already solve using this tutorial. It is well explained. thanks by the for guidance @SunilSufficient
@Sunil androidbash.com/firebase-push-notification-android I try this tutorial, but there is some restrictions Like Notification with image an fetched from the url is displayed to the user (with BigImage). But if the app is background or killed state, then onMessageReceived () method will not be called. Hence a simple notification containing just a message will be displayed in the notification bar on your Android device.Backstitch
Its working, but it takes a few seconds to display the notification, it doesn't display immediately. Is there some way that, notification generate immediately. Is there any way to display notification immediately, and load image from url meanwhile?Bushwhacker
@PrashantGosai is there any way to show image in background state also ?Kight
Why did you have the notification code in Async Task?Trutko
@Micklo_Nerd, We are loading image in background thread that's why we've used Async Task. You probably can use Rx java instead to eliminate the code.Miele
Okay, i get it now. ThanksTrutko
S
28

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 and 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")
            }
        )
}
Self answered 4/8, 2019 at 3:7 Comment(4)
Very solid answer! Works as is in Kotlin!Strove
I have applied this code in two apps and has worked great. Thanks for sharing your experience.Self
This is more lucrative.Lenette
I edited the answer by appending RxJava solution as well.Self
S
10

Since the image is loaded from the Internet, it should be done async in a background thread. Either use an async task or Glide API (for efficient image loading).

To load image notification, you need to use "NotificationCompat.BigPictureStyle()". This requires a bitmap (which has to be extracted from the image URL).

Most of the API's and methods of Glide are now deprecated. The below is working with Glide 4.9 and up to Android 10.

 // Load the bitmap from the image URL on a background thread and display an 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());
}
Stowaway answered 30/10, 2019 at 17:51 Comment(1)
Is there any special purpose to using setPriority twice?Farris
F
6

An explanation for Hiren Patel's answer to make it easier to understand and implement.

---------------------Theory------------------------

The problem can be abstracted in two-step solution, namely:

  1. Get the image from a URL

  2. Decode the image and pass it to the notification builder

  3. Get the image from a URL
    InputStream in = new URL("Image URL goes here, e.g., http://gg.com/profile.jpg").openStream();

  4. Decode and pass to the notification
    Bitmap bmp = null; # Create a null BMP container that will be used to hold the decoded image
    bmp = BitmapFactory.decodeStream(in); # Save the image into the container

Voilà! Once you build the image and save it in the variable bmp, you can call it on the 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);
Famed answered 8/10, 2018 at 21:36 Comment(1)
you should call input stream in = new Url .....() in a asynctask , never perform network request in the main threadAphotic
F
0

Using the 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);
Faltboat answered 16/6, 2019 at 5:24 Comment(0)
S
0

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?
                        }
                    });
        }
    });
Sapid answered 15/7, 2020 at 7:58 Comment(0)
B
0

A 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")
Bronnie answered 21/2, 2021 at 14:55 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.