Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Set Course for Notifications… Engage! (Toronto Android Developers Meetup)

Eric Fung
September 14, 2016

Set Course for Notifications… Engage! (Toronto Android Developers Meetup)

With only a minimum of coding, Firebase provides your Android app with simple push notifications and collects analytics to measure user engagement. However, if you need to customize the notifications or their behavior, you need to implement everything yourself. For a recent Android news app I developed, I wanted to use push messaging to inform users when new articles were published. This talk will describe how I used three Firebase tools (Notifications, Cloud Messaging, and Analytics) to implement custom notifications and analytics to track their performance.

Presented at Toronto Android Developers Meetup (https://www.meetup.com/ToAndroidDev/events/233664313/).

Video available at https://www.youtube.com/watch?v=7btudq2PCas

Eric Fung

September 14, 2016
Tweet

More Decks by Eric Fung

Other Decks in Programming

Transcript

  1. Cloud Messaging • Service that reliably delivers messages • Optional

    application server integration • Multiplatform client SDKs
  2. Notifications • Graphical console • Built on top of FCM

    • Designed for marketing and engagement • Built-in analytics
  3. Android client SDK • Permissions added to manifest • Creating

    registration token, and refresh • Broadcast receiver to listen for notifications • Display notification if in background • Topic management • Analytics
  4. Great! But… • Different notification style? • Grouping and summary?

    • Background send-to-sync? • Analytics and reporting?
  5. Notification Messages { "to" : "/topics/new_articles", "notification" : { "body"

    : "How to Grow a Business with Email Marketing", "title" : "New article available", "icon" : "myicon" } } • Predefined keys • Displayed automatically if backgrounded • Console only sends this kind
  6. Data Messages { "to" : "/topics/new_articles", "data" : { "object_type"

    : "article", "status" : "new", "item_id" : 14142135 } } • Freeform keys and values • App decides what to do • Console cannot send this kind
  7. Data Messages • Completely handled by app • So no

    automatic analytics ! • And no funnel analysis ! !
  8. Grouping Notifications NotificationCompat.Builder builder = new NotificationCompat.Builder(context) .setContentTitle("New article published")

    .setContentText("Time Management Tips") .setSmallIcon(R.drawable.ic_stat_notify) .setDeleteIntent(getDeleteIntent(article._id())) .setContentIntent(getContentIntent(article._id())) .setGroup(GROUP_NEW_ARTICLE) // ⾢⾢⾢⾢⾢ ;
  9. Summary Notification NotificationCompat.Builder builder = new NotificationCompat.Builder(context) .setContentTitle(res.getQuantityString(R.plurals.summary_title, numActive, numActive))

    .setSmallIcon(R.drawable.ic_stat_notify) .setStyle(inboxStyle) .setGroup(GROUP_NEW_ARTICLE) // ⾢⾢⾢⾢⾢ .setGroupSummary(true) // ⾢⾢⾢⾢⾢ ;
  10. Being a Good App Citizen • Messages delivered by FCM

    are nearly instantaneous • If devices all sync at the same time, server may be overwhelmed • Clients should jitter the start of their sync • Conserve battery by batching requests that require network
  11. Scheduling Background Sync • JobScheduler API has been ported to

    older platforms • Firebase JobDispatcher • Recommended for new apps • Source available, but no release • GCM Network Manager • Uses Play Services • No new development
  12. Scheduling Background Sync • Jobs described with requirements for network

    and timing • Scheduler tries to batch and defer as long as possible • See https://developer.android.com/topic/performance/ scheduling.html
  13. SyncBackgroundService.java @Override public void onMessageReceived(RemoteMessage remoteMessage) { Map<String, String> data

    = remoteMessage.getData(); long articleId = parseIdFromData(data); // Remember that we want to show notification for articleId syncHelper.scheduleArticleFetch(rng.nextInt(SYNC_MIN) + 1, SYNC_MAX); }
  14. MyGcmTaskService.java @Override public int onRunTask(TaskParams taskParams) { Bundle extras =

    taskParams.getExtras(); … api.fetchLatest(…); // Alert observers that sync has completed, e.g. via event bus }
  15. Recording Analytics • Automatically collected events use reserved names •

    Our events will have to use different names • We will also try to create our own funnels
  16. Detect Foreground or Background • Typically, do something if in

    foreground, otherwise post notification • Ordered Broadcast https://commonsware.com/blog/2010/08/11/activity- notification-ordered-broadcast.html • Application.onTrimMemory() and ActivityLifecycleCallbacks: http:// www.developerphil.com/no-you-can-not-override-the- home-button-but-you-dont-have-to/
  17. notification_receive, notification_foreground @Override public void onMessageReceived(RemoteMessage remoteMessage) { … Bundle

    params = …; boolean isBackground = ((MyApp)getApplication()).isBackground(); firebaseAnalytics.logEvent(isBackground ? "notif_receive" : "notif_foreground", params);
  18. notification_receive, notification_foreground @Override public void onMessageReceived(RemoteMessage remoteMessage) { … Bundle

    params = …; // Optional: Put item ID and other attributes into params boolean isBackground = ((MyApp)getApplication()).isBackground(); firebaseAnalytics.logEvent(isBackground ? "notif_receive" : "notif_foreground", params);
  19. Opening Notification final int flags = PendingIntent.FLAG_UPDATE_CURRENT; Intent serviceIntent =

    new Intent(NotificationActionService.ACTION_TAPPED, null, context, NotificationActionService.class); serviceIntent.putExtra(NotificationActionService.EXTRA_ITEM_ID, itemId); Intent intent = PendingIntent.getService(context.getApplicationContext(), REQUEST_CODE_TAPPED, serviceIntent, flags); NotificationCompat.Builder builder = new NotificationCompat.Builder(context) .setContentIntent(intent) …
  20. Opening Notification final int flags = PendingIntent.FLAG_UPDATE_CURRENT; Intent serviceIntent =

    new Intent(NotificationActionService.ACTION_TAPPED, // ⾢⾢⾢⾢⾢ null, context, NotificationActionService.class); serviceIntent.putExtra(NotificationActionService.EXTRA_ITEM_ID, itemId); Intent intent = PendingIntent.getService(context.getApplicationContext(), REQUEST_CODE_TAPPED, serviceIntent, flags); NotificationCompat.Builder builder = new NotificationCompat.Builder(context) .setContentIntent(intent) …
  21. NotificationActionService.java public class NotificationActionService extends IntentService { @Override protected void

    onHandleIntent(Intent intent) { … switch (intent.getAction()) { case ACTION_TAPPED: … firebaseAnalytics.logEvent("notif_open", params); } }
  22. User Properties • Attributes that describe user, recorded on every

    event • Some already collected automatically • Need to register properties in Firebase Console • For notifications: • Whether they are enabled for the app • Whether they are blocked • Any related experiment variants
  23. Notification Settings • Make sure you have a Settings activity

    to enable notifications • Defined with android.intent.category.NOTIFICATION_PREFERENCES • When value changed, log user property
  24. Notification Settings @Override public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {

    … boolean notifsEnabled = sharedPreferences.getBoolean("pref_key_notifs", false); firebaseAnalytics.setUserProperty("app_notifs_enabled", Boolean.toString(notifsEnabled)) }
  25. System Notification Settings • boolean notifManagerCompat.areNotificationsEnabled() (Support Library 24.0.0+) •

    ⾠ If older than API 19, will always return true • Record as user property firebaseAnalytics.setUserProperty("system_notifs_enabled", nmc.areNotificationsEnabled())
  26. Audiences • Can only filter on one property at a

    time • Audiences segment user base by combining events, parameters and properties
  27. Notification Funnel Analysis • Filter by audience (optional) • Define

    two events, e.g. notif_receive, notif_open • Not the same as the funnel analysis in Firebase Notifications though…
  28. Analytics Limitations • Event parameters aren't available for reporting •

    Parameters can only be specified in Audiences • For funnel analysis, we can't limit events to a specific parameter
  29. Improving Analytics • Feature request that Firebase engineers know about

    • (And hopefully working on) • Can also stream data to BigQuery for deep analysis
  30. Sending Notifications to FCM • Don't need application server •

    Use topic messaging • All clients subscribed to one topic firebase.subscribeToTopic("all_new_articles");
  31. Sending Notifications to FCM Example using cURL BODY=$(cat <<END {

    "data": { "key1": "val1", "key2": "val2" }, "to" : "/topics/all_new_articles", } END ) curl \ --header "Authorization: key=$FCM_SERVER_KEY" \ --header "Content-Type: application/json" \ https://fcm.googleapis.com/fcm/send \ --data-ascii "${BODY}"
  32. Wrap Up • Firebase Notifications give you a basic implementation

    • For more complex needs, get to know Firebase Cloud Messaging • Firebase didn't currently save me any code • While you can get similar analytics, it's not as easy to do