Sailthru Mobile supports Android Rich Notifications out of the box for image payloads.

When creating image messages, Android will automatically add the image's thumbnail to the notification with an expanded layout showing a larger image.

11051105

Creating a message with an image inside the Carnival platform.

709709

An Android Rich Notification on device created from the message.

You can extend or change this default behavior by adding a NotificationCompat.Extender and implementing your own notification style. This is useful if you want to add media controls, or you'd like to render the notification body with an expanded view.

import android.support.v4.app.NotificationCompat;
public class RichPushNotificationExtender implements NotificationCompat.Extender {
  protected static String TAG = "RichPushNotificationExtender";
  protected static String KEY_PAYLOAD_ALERT = "alert";
  protected static String KEY_PAYLOAD_TITLE = "title";
  protected static String KEY_PAYLOAD_IMAGE_URL = "image_url";
  
  @Override
  public NotificationCompat.Builder extend(NotificationCompat.Builder builder) {
    Bundle bundle = builder.getExtras();
    Context context = builder.mContext;
    
    String imageUrl = bundle.getString(KEY_PAYLOAD_IMAGE_URL);
    if (TextUtils.isEmpty(imageUrl)) {
        // Keep Carnival style if there's not "image_url" on payload
        return builder.extend(new CarnivalNotificationExtender(context));
    }
    
    builder.setContentTitle(context.getResources().getString(R.string.app_name))
           .setSmallIcon(R.drawable.carnival_bg_notification)
           .setContentText(bundle.getString(KEY_PAYLOAD_ALERT));
           
    Bitmap image = fetchImageForMessage(imageUrl);
    
    NotificationCompat.BigPictureStyle largeFormatStyle = new NotificationCompat.BigPictureStyle()
      .bigPicture(image)
      .setBigContentTitle(context.getResources().getString(R.string.app_name))
      .setSummaryText(bundle.getString(KEY_PAYLOAD_TITLE))
      .bigLargeIcon(image);
    builder.setLargeIcon(image);
    builder.setStyle(largeFormatStyle);

    return builder;
  }
  
  // code used to load image from url
  private @Nullable Bitmap fetchImageForMessage(@NonNull String imageUrl) {
      URL url;
      try {
          url = new URL(imageUrl);
      } catch (MalformedURLException e) {
          Log.e(TAG, "Malformed image URL in Push Payload: " + e.getLocalizedMessage());
          return null;
      }

      AsyncTask<URL, Void, Bitmap> task = new AsyncTask<URL, Void, Bitmap>() {

          @Override
          protected Bitmap doInBackground(URL... params) {
              try {
                  URL url = params[0];
                  HttpURLConnection connection = (HttpURLConnection) url.openConnection();
                  connection.setDoInput(true);
                  connection.connect();
                  InputStream input = connection.getInputStream();
                  return BitmapFactory.decodeStream(input);
              } catch (IOException e) {
                  Log.e(TAG, "IO Error loading Message image:" + e.getLocalizedMessage());
                  return Bitmap.createBitmap(1, 1, Bitmap.Config.ALPHA_8);
              }
          }

      }.execute(url);
      try {
          return task.get(5, TimeUnit.SECONDS);
      } catch (Exception e) {
          Log.e(TAG, "Failed to wait for Message Image in RichPushNotificationExtender: " + e.getMessage());
          return null;
      }
  }
   
}

Then add your extender to NotificationConfig

public class MyApplication extends Application {
  @Override
  public void onCreate() {
    super.onCreate();

    Carnival.startEngine(getApplicationContext(), "your sdk key");

    NotificationConfig notificationConfig = new NotificationConfig();
    notificationConfig.addNotificationExtender(new RichPushNotificationExtender());
    Carnival.setNotificationConfig(notificationConfig);
  }
}