{"metadata":{"image":[],"title":"","description":""},"api":{"url":"","auth":"required","params":[],"results":{"codes":[]},"settings":"","apiSetting":null},"next":{"description":"","pages":[]},"title":"Android: Rich Notifications","type":"basic","slug":"rich-notifications","excerpt":"","body":"Sailthru Mobile supports Android Rich Notifications out of the box for image payloads.\n\n\n### Send Rich Notifications\n\nWhen creating an in-app message with push notification, the image added to the in-app message will automatically be delivered to the Android device and displayed as a rich push notification.\n\nAlternatively, [rich notifications can be sent using the API as per the Rich Push example](https://docs.mobile.sailthru.com/docs/notifications).\n[block:image]\n{\n  \"images\": [\n    {\n      \"image\": [\n        \"https://files.readme.io/131d415-Screen_Shot_2018-05-08_at_1.45.22_PM.png\",\n        \"Screen Shot 2018-05-08 at 1.45.22 PM.png\",\n        1105,\n        523,\n        \"#e2e8ed\"\n      ],\n      \"caption\": \"Creating a message with an image inside the Sailthru Mobile platform.\",\n      \"border\": true\n    }\n  ]\n}\n[/block]\n\n[block:image]\n{\n  \"images\": [\n    {\n      \"image\": [\n        \"https://files.readme.io/e9cb256-Screenshot_20170324-063420.png\",\n        \"Screenshot_20170324-063420.png\",\n        709,\n        508,\n        \"#e9dfdd\"\n      ],\n      \"caption\": \"An Android Rich Notification on device created from the message.\",\n      \"sizing\": \"smart\",\n      \"border\": true\n    }\n  ]\n}\n[/block]\n### Extend or change default behavior \n\nYou 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.\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"\\nimport android.support.v4.app.NotificationCompat;\\npublic class RichPushNotificationExtender implements NotificationCompat.Extender {\\n  protected static String TAG = \\\"RichPushNotificationExtender\\\";\\n  protected static String KEY_PAYLOAD_ALERT = \\\"alert\\\";\\n  protected static String KEY_PAYLOAD_TITLE = \\\"title\\\";\\n  protected static String KEY_PAYLOAD_IMAGE_URL = \\\"image_url\\\";\\n  protected static String KEY_PAYLOAD_ST = \\\"_st\\\";\\n  \\n  :::at:::Override\\n  public NotificationCompat.Builder extend(NotificationCompat.Builder builder) {\\n    Bundle bundle = builder.getExtras();\\n    Context context = builder.mContext;\\n    \\n    // For Image or Video in-app messages, we will send the media URL in the\\n    // _st payload\\n    String imageUrl = null;\\n    String stString = bundle.getString(KEY_PAYLOAD_ST);\\n    if(stString != null && !stString.isEmpty()) {\\n      try {\\n        JSONObject stPayload = new JSONObject(stString);\\n        if (!stPayload.isNull(KEY_PAYLOAD_IMAGE_URL)) {\\n          imageUrl = stPayload.getString(KEY_PAYLOAD_IMAGE_URL);\\n        }\\n      } catch (JSONException e) {\\n        Log.e(TAG, \\\"Unable to get \\\" + KEY_PAYLOAD_IMAGE_URL + \\\" from '_st' payload: \\\" + stString + \\\".\\\\n\\\" + e.getLocalizedMessage());\\n      }\\n    }\\n    \\n    builder.setContentTitle(context.getResources().getString(R.string.app_name))\\n      .setSmallIcon(R.drawable.st_bg_notification)\\n      .setContentText(bundle.getString(KEY_PAYLOAD_ALERT));\\n\\n    if(imageUrl != null && !imageUrl.isEmpty()) {\\n      Bitmap image = fetchImageForMessage(imageUrl);\\n\\n      NotificationCompat.BigPictureStyle largeFormatStyle = new NotificationCompat.BigPictureStyle()\\n        .bigPicture(image)\\n        .setBigContentTitle(context.getResources().getString(R.string.app_name))\\n        .setSummaryText(bundle.getString(KEY_PAYLOAD_TITLE))\\n        .bigLargeIcon(image);\\n      builder.setLargeIcon(image);\\n      builder.setStyle(largeFormatStyle);\\n    }\\n\\n    return builder;\\n  }\\n  \\n  // code used to load image from url\\n  private @Nullable Bitmap fetchImageForMessage(@NonNull String imageUrl) {\\n      URL url;\\n      try {\\n          url = new URL(imageUrl);\\n      } catch (MalformedURLException e) {\\n          Log.e(TAG, \\\"Malformed image URL in Push Payload: \\\" + e.getLocalizedMessage());\\n          return null;\\n      }\\n\\n      AsyncTask<URL, Void, Bitmap> task = new AsyncTask<URL, Void, Bitmap>() {\\n\\n          @Override\\n          protected Bitmap doInBackground(URL... params) {\\n              try {\\n                  URL url = params[0];\\n                  HttpURLConnection connection = (HttpURLConnection) url.openConnection();\\n                  connection.setDoInput(true);\\n                  connection.connect();\\n                  InputStream input = connection.getInputStream();\\n                  return BitmapFactory.decodeStream(input);\\n              } catch (IOException e) {\\n                  Log.e(TAG, \\\"IO Error loading Message image:\\\" + e.getLocalizedMessage());\\n                  return Bitmap.createBitmap(1, 1, Bitmap.Config.ALPHA_8);\\n              }\\n          }\\n\\n      }.execute(url);\\n      try {\\n          return task.get(5, TimeUnit.SECONDS);\\n      } catch (Exception e) {\\n          Log.e(TAG, \\\"Failed to wait for Message Image in RichPushNotificationExtender: \\\" + e.getMessage());\\n          return null;\\n      }\\n  }\\n   \\n}\",\n      \"language\": \"java\",\n      \"name\": \"Android (Java)\"\n    },\n    {\n      \"code\": \"import androidx.core.app.NotificationCompat\\n\\nclass RichPushNotificationExtender : NotificationCompat.Extender {\\n    override fun extend(builder: NotificationCompat.Builder): NotificationCompat.Builder {\\n        val bundle: Bundle = builder.getExtras()\\n        val context: Context = builder.mContext\\n\\n        // For Image or Video in-app messages, we will send the media URL in the\\n        // _st payload\\n        var imageUrl: String? = null\\n        val stString = bundle.getString(KEY_PAYLOAD_ST)\\n        if (stString != null && !stString.isEmpty()) {\\n            try {\\n                val stPayload = JSONObject(stString)\\n                if (!stPayload.isNull(KEY_PAYLOAD_IMAGE_URL)) {\\n                    imageUrl = stPayload.getString(KEY_PAYLOAD_IMAGE_URL)\\n                }\\n            } catch (e: JSONException) {\\n                Log.e(TAG, \\\"\\\"\\\"Unable to get $KEY_PAYLOAD_IMAGE_URL from '_st' payload: $stString.\\n${e.getLocalizedMessage()}\\\"\\\"\\\")\\n            }\\n        }\\n        builder.setContentTitle(context.resources.getString(R.string.app_name))\\n                .setSmallIcon(R.drawable.st_bg_notification)\\n                .setContentText(bundle.getString(KEY_PAYLOAD_ALERT))\\n        if (imageUrl != null && !imageUrl.isEmpty()) {\\n            val image = fetchImageForMessage(imageUrl)\\n            val largeFormatStyle: NotificationCompat.BigPictureStyle = NotificationCompat.BigPictureStyle()\\n                    .bigPicture(image)\\n                    .setBigContentTitle(context.resources.getString(R.string.app_name))\\n                    .setSummaryText(bundle.getString(KEY_PAYLOAD_TITLE))\\n                    .bigLargeIcon(image)\\n            builder.setLargeIcon(image)\\n            builder.setStyle(largeFormatStyle)\\n        }\\n        return builder\\n    }\\n\\n    // code used to load image from url\\n    private fun fetchImageForMessage(imageUrl: String): Bitmap? {\\n        val url: URL\\n        try {\\n            url = URL(imageUrl)\\n        } catch (e: MalformedURLException) {\\n            Log.e(TAG, \\\"Malformed image URL in Push Payload: \\\" + e.getLocalizedMessage())\\n            return null\\n        }\\n        val task: AsyncTask<URL?, Void?, Bitmap?> = object : AsyncTask<URL?, Void?, Bitmap>() {\\n            override fun doInBackground(vararg params: URL): Bitmap {\\n                return try {\\n                    val url: URL = params[0]\\n                    val connection: HttpURLConnection = url.openConnection() as HttpURLConnection\\n                    connection.setDoInput(true)\\n                    connection.connect()\\n                    val input: InputStream = connection.getInputStream()\\n                    BitmapFactory.decodeStream(input)\\n                } catch (e: IOException) {\\n                    Log.e(TAG, \\\"IO Error loading Message image:\\\" + e.getLocalizedMessage())\\n                    Bitmap.createBitmap(1, 1, Bitmap.Config.ALPHA_8)\\n                }\\n            }\\n        }.execute(url)\\n        return try {\\n            task[5, TimeUnit.SECONDS]\\n        } catch (e: Exception) {\\n            Log.e(TAG, \\\"Failed to wait for Message Image in RichPushNotificationExtender: \\\" + e.message)\\n            null\\n        }\\n    }\\n\\n    companion object {\\n        protected var TAG = \\\"RichPushNotificationExtender\\\"\\n        protected var KEY_PAYLOAD_ALERT = \\\"alert\\\"\\n        protected var KEY_PAYLOAD_TITLE = \\\"title\\\"\\n        protected var KEY_PAYLOAD_IMAGE_URL = \\\"image_url\\\"\\n        protected var KEY_PAYLOAD_ST = \\\"_st\\\"\\n    }\\n}\",\n      \"language\": \"kotlin\"\n    }\n  ]\n}\n[/block]\nThen add your extender to `NotificationConfig`\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"public class MyApplication extends Application {\\n  @Override\\n  public void onCreate() {\\n    super.onCreate();\\n\\n    SailthruMobile sailthruMobile = new SailthruMobile();\\n    sailthruMobile.startEngine(getApplicationContext(), \\\"your sdk key\\\");\\n\\n    NotificationConfig notificationConfig = new NotificationConfig();\\n    notificationConfig.addNotificationExtender(new RichPushNotificationExtender());\\n    sailthruMobile.setNotificationConfig(notificationConfig);\\n  }\\n}\",\n      \"language\": \"java\"\n    },\n    {\n      \"code\": \"class MyApplication : Application() {\\n    fun onCreate() {\\n        super.onCreate()\\n        val sailthruMobile = SailthruMobile()\\n        sailthruMobile.startEngine(applicationContext, \\\"your sdk key\\\")\\n        val notificationConfig = NotificationConfig()\\n        notificationConfig.addNotificationExtender(RichPushNotificationExtender())\\n        sailthruMobile.setNotificationConfig(notificationConfig)\\n    }\\n}\",\n      \"language\": \"kotlin\"\n    }\n  ]\n}\n[/block]","updates":[],"order":1,"isReference":false,"hidden":false,"sync_unique":"","link_url":"","link_external":false,"_id":"5e6156bf5e4a51006dcd815e","parentDoc":null,"githubsync":"","createdAt":"2016-05-19T02:48:53.845Z","project":"55e67aaa9cc7c62b00c4a1ea","__v":9,"category":{"sync":{"isSync":false,"url":""},"pages":[],"title":"Android Techniques","slug":"android-techniques","order":9999,"from_sync":false,"reference":false,"_id":"5e69868cbd5dcb006b35867b","createdAt":"2020-03-12T00:47:08.696Z","version":"5e6156bf5e4a51006dcd818c","project":"55e67aaa9cc7c62b00c4a1ea","__v":0},"user":"56035f358d58900d0051e976","version":{"version":"1.5","version_clean":"1.5.0","codename":"ST Rebrand","is_stable":true,"is_beta":false,"is_hidden":false,"is_deprecated":false,"categories":["5e6156bf5e4a51006dcd8120","5e6156bf5e4a51006dcd8121","5e6156bf5e4a51006dcd8122","5e6156bf5e4a51006dcd8123","5e6156bf5e4a51006dcd8124","561c61b4ad272c0d00a892df","586c014c0abf1d0f000d04d4","58991d2ad207df0f0002186b","5e6156bf5e4a51006dcd8125","5e6156bf5e4a51006dcd8126","5af0fe494ca2730003cbc98a","5af0fe55ec80af0003804ca2","5e69868cbd5dcb006b35867b","5e6986ca2c6652006791b6e8"],"_id":"5e6156bf5e4a51006dcd818c","project":"55e67aaa9cc7c62b00c4a1ea","__v":2,"forked_from":"5b720760c44b7600034b7a08","createdAt":"2015-09-02T04:27:23.612Z","releaseDate":"2015-09-02T04:27:23.612Z"}}

Android: Rich Notifications


Sailthru Mobile supports Android Rich Notifications out of the box for image payloads. ### Send Rich Notifications When creating an in-app message with push notification, the image added to the in-app message will automatically be delivered to the Android device and displayed as a rich push notification. Alternatively, [rich notifications can be sent using the API as per the Rich Push example](https://docs.mobile.sailthru.com/docs/notifications). [block:image] { "images": [ { "image": [ "https://files.readme.io/131d415-Screen_Shot_2018-05-08_at_1.45.22_PM.png", "Screen Shot 2018-05-08 at 1.45.22 PM.png", 1105, 523, "#e2e8ed" ], "caption": "Creating a message with an image inside the Sailthru Mobile platform.", "border": true } ] } [/block] [block:image] { "images": [ { "image": [ "https://files.readme.io/e9cb256-Screenshot_20170324-063420.png", "Screenshot_20170324-063420.png", 709, 508, "#e9dfdd" ], "caption": "An Android Rich Notification on device created from the message.", "sizing": "smart", "border": true } ] } [/block] ### Extend or change default behavior 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. [block:code] { "codes": [ { "code": "\nimport android.support.v4.app.NotificationCompat;\npublic class RichPushNotificationExtender implements NotificationCompat.Extender {\n protected static String TAG = \"RichPushNotificationExtender\";\n protected static String KEY_PAYLOAD_ALERT = \"alert\";\n protected static String KEY_PAYLOAD_TITLE = \"title\";\n protected static String KEY_PAYLOAD_IMAGE_URL = \"image_url\";\n protected static String KEY_PAYLOAD_ST = \"_st\";\n \n @Override\n public NotificationCompat.Builder extend(NotificationCompat.Builder builder) {\n Bundle bundle = builder.getExtras();\n Context context = builder.mContext;\n \n // For Image or Video in-app messages, we will send the media URL in the\n // _st payload\n String imageUrl = null;\n String stString = bundle.getString(KEY_PAYLOAD_ST);\n if(stString != null && !stString.isEmpty()) {\n try {\n JSONObject stPayload = new JSONObject(stString);\n if (!stPayload.isNull(KEY_PAYLOAD_IMAGE_URL)) {\n imageUrl = stPayload.getString(KEY_PAYLOAD_IMAGE_URL);\n }\n } catch (JSONException e) {\n Log.e(TAG, \"Unable to get \" + KEY_PAYLOAD_IMAGE_URL + \" from '_st' payload: \" + stString + \".\\n\" + e.getLocalizedMessage());\n }\n }\n \n builder.setContentTitle(context.getResources().getString(R.string.app_name))\n .setSmallIcon(R.drawable.st_bg_notification)\n .setContentText(bundle.getString(KEY_PAYLOAD_ALERT));\n\n if(imageUrl != null && !imageUrl.isEmpty()) {\n Bitmap image = fetchImageForMessage(imageUrl);\n\n NotificationCompat.BigPictureStyle largeFormatStyle = new NotificationCompat.BigPictureStyle()\n .bigPicture(image)\n .setBigContentTitle(context.getResources().getString(R.string.app_name))\n .setSummaryText(bundle.getString(KEY_PAYLOAD_TITLE))\n .bigLargeIcon(image);\n builder.setLargeIcon(image);\n builder.setStyle(largeFormatStyle);\n }\n\n return builder;\n }\n \n // code used to load image from url\n private @Nullable Bitmap fetchImageForMessage(@NonNull String imageUrl) {\n URL url;\n try {\n url = new URL(imageUrl);\n } catch (MalformedURLException e) {\n Log.e(TAG, \"Malformed image URL in Push Payload: \" + e.getLocalizedMessage());\n return null;\n }\n\n AsyncTask<URL, Void, Bitmap> task = new AsyncTask<URL, Void, Bitmap>() {\n\n @Override\n protected Bitmap doInBackground(URL... params) {\n try {\n URL url = params[0];\n HttpURLConnection connection = (HttpURLConnection) url.openConnection();\n connection.setDoInput(true);\n connection.connect();\n InputStream input = connection.getInputStream();\n return BitmapFactory.decodeStream(input);\n } catch (IOException e) {\n Log.e(TAG, \"IO Error loading Message image:\" + e.getLocalizedMessage());\n return Bitmap.createBitmap(1, 1, Bitmap.Config.ALPHA_8);\n }\n }\n\n }.execute(url);\n try {\n return task.get(5, TimeUnit.SECONDS);\n } catch (Exception e) {\n Log.e(TAG, \"Failed to wait for Message Image in RichPushNotificationExtender: \" + e.getMessage());\n return null;\n }\n }\n \n}", "language": "java", "name": "Android (Java)" }, { "code": "import androidx.core.app.NotificationCompat\n\nclass RichPushNotificationExtender : NotificationCompat.Extender {\n override fun extend(builder: NotificationCompat.Builder): NotificationCompat.Builder {\n val bundle: Bundle = builder.getExtras()\n val context: Context = builder.mContext\n\n // For Image or Video in-app messages, we will send the media URL in the\n // _st payload\n var imageUrl: String? = null\n val stString = bundle.getString(KEY_PAYLOAD_ST)\n if (stString != null && !stString.isEmpty()) {\n try {\n val stPayload = JSONObject(stString)\n if (!stPayload.isNull(KEY_PAYLOAD_IMAGE_URL)) {\n imageUrl = stPayload.getString(KEY_PAYLOAD_IMAGE_URL)\n }\n } catch (e: JSONException) {\n Log.e(TAG, \"\"\"Unable to get $KEY_PAYLOAD_IMAGE_URL from '_st' payload: $stString.\n${e.getLocalizedMessage()}\"\"\")\n }\n }\n builder.setContentTitle(context.resources.getString(R.string.app_name))\n .setSmallIcon(R.drawable.st_bg_notification)\n .setContentText(bundle.getString(KEY_PAYLOAD_ALERT))\n if (imageUrl != null && !imageUrl.isEmpty()) {\n val image = fetchImageForMessage(imageUrl)\n val largeFormatStyle: NotificationCompat.BigPictureStyle = NotificationCompat.BigPictureStyle()\n .bigPicture(image)\n .setBigContentTitle(context.resources.getString(R.string.app_name))\n .setSummaryText(bundle.getString(KEY_PAYLOAD_TITLE))\n .bigLargeIcon(image)\n builder.setLargeIcon(image)\n builder.setStyle(largeFormatStyle)\n }\n return builder\n }\n\n // code used to load image from url\n private fun fetchImageForMessage(imageUrl: String): Bitmap? {\n val url: URL\n try {\n url = URL(imageUrl)\n } catch (e: MalformedURLException) {\n Log.e(TAG, \"Malformed image URL in Push Payload: \" + e.getLocalizedMessage())\n return null\n }\n val task: AsyncTask<URL?, Void?, Bitmap?> = object : AsyncTask<URL?, Void?, Bitmap>() {\n override fun doInBackground(vararg params: URL): Bitmap {\n return try {\n val url: URL = params[0]\n val connection: HttpURLConnection = url.openConnection() as HttpURLConnection\n connection.setDoInput(true)\n connection.connect()\n val input: InputStream = connection.getInputStream()\n BitmapFactory.decodeStream(input)\n } catch (e: IOException) {\n Log.e(TAG, \"IO Error loading Message image:\" + e.getLocalizedMessage())\n Bitmap.createBitmap(1, 1, Bitmap.Config.ALPHA_8)\n }\n }\n }.execute(url)\n return try {\n task[5, TimeUnit.SECONDS]\n } catch (e: Exception) {\n Log.e(TAG, \"Failed to wait for Message Image in RichPushNotificationExtender: \" + e.message)\n null\n }\n }\n\n companion object {\n protected var TAG = \"RichPushNotificationExtender\"\n protected var KEY_PAYLOAD_ALERT = \"alert\"\n protected var KEY_PAYLOAD_TITLE = \"title\"\n protected var KEY_PAYLOAD_IMAGE_URL = \"image_url\"\n protected var KEY_PAYLOAD_ST = \"_st\"\n }\n}", "language": "kotlin" } ] } [/block] Then add your extender to `NotificationConfig` [block:code] { "codes": [ { "code": "public class MyApplication extends Application {\n @Override\n public void onCreate() {\n super.onCreate();\n\n SailthruMobile sailthruMobile = new SailthruMobile();\n sailthruMobile.startEngine(getApplicationContext(), \"your sdk key\");\n\n NotificationConfig notificationConfig = new NotificationConfig();\n notificationConfig.addNotificationExtender(new RichPushNotificationExtender());\n sailthruMobile.setNotificationConfig(notificationConfig);\n }\n}", "language": "java" }, { "code": "class MyApplication : Application() {\n fun onCreate() {\n super.onCreate()\n val sailthruMobile = SailthruMobile()\n sailthruMobile.startEngine(applicationContext, \"your sdk key\")\n val notificationConfig = NotificationConfig()\n notificationConfig.addNotificationExtender(RichPushNotificationExtender())\n sailthruMobile.setNotificationConfig(notificationConfig)\n }\n}", "language": "kotlin" } ] } [/block]