Enhanced Notifications in Android N with Direct Reply

James Montemagno

One of my favorite parts of Android has to be it’s notification system enabling developers to directly connect to their users outside of the main application. With the launch of Android N, notifications are getting a visual make-over including a new material design with re-arranged and sized content to make them easier to digest and some new details specific for Android N such as app name and an expander. Here is a nice visual overview of the change from Android M to N:

Android N Notification

Visuals aren’t the only thing getting updated in Android N, as there are a bunch of great new features for developers to take advantage of. Bundled Notifications allow developers to group notifications together by using the Builder.SetGroup() method. Custom Views have been enhanced and it is now possible to use the system notification headers, actions, and expanded layouts with a custom view. Finally, my favorite new feature has to be Direct Reply, allowing users to reply to a message within a notification so they don’t even have to open up the application. This is similar to how Android Wear applications could send text back to the main application.

2016-09-19_1810

Getting Started

In previous versions of Android, all developers could handle was notification and action tap events, which would launch an Activity or a service/broadcast receiver when using an action. The idea of Direct Reply is to extend an action with a RemoteInput to enable users to reply to a message without having to launch the application. It’s best practice to handle responding to messages inside of an Activity as the user may decide to tap on the notification or may be on an older operating system.

A pre-requisite to implementing Direct Reply is that we must have a broadcast receiver or service implemented that can receive and process the incoming reply from the user. For this example, we’ll be launching a notification from our MainActivity that will send an Intent with a value of “com.xamarin.directreply.REPLY” that our broadcast receiver will filter on.

First, ensure that the latest Android Support Library v4 NuGet is installed in the Android application to use the compatibility mode for notifications.

In our MainActivity, we’ll create a few constant strings that can be referenced later in the code:

int requestCode = 0;

public const string REPLY_ACTION = "com.xamarin.directreply.REPLY";
public const string KEY_TEXT_REPLY = "key_text_reply";
public const string REQUEST_CODE = "request_code";

Create a Pending Intent

An Android Pending Intent is a description of an Intent and target action to perform with it. In this case, we want to create one that will trigger our reply action if the user is on Android N, or that will launch the Main Activity if the user is on an older devices.

Intent intent = null;
PendingIntent pendingIntent= null;
//If Android N then enable direct reply, else launch main activity.
if ((int)Build.VERSION.SdkInt >= 24)
{
    intent = new Intent(REPLY_ACTION)
			.AddFlags(ActivityFlags.IncludeStoppedPackages)
			.SetAction(REPLY_ACTION)
			.PutExtra(REQUEST_CODE, requestCode);

    pendingIntent = PendingIntent.GetBroadcast(this, requestCode, intent, PendingIntentFlags.UpdateCurrent);
}
else
{
    intent = new Intent(this, typeof(MainActivity));
    intent.AddFlags(ActivityFlags.ClearTop | ActivityFlags.NewTask);

    pendingIntent = PendingIntent.GetActivity(this, requestCode, intent, PendingIntentFlags.UpdateCurrent);
}

Create and Attach RemoteInput

The key to direct reply is to create and attach a RemoteInput, which will tell Android that this action that we’re adding is a direct reply and thus should allow the user to enter text.

var replyText = "Reply to message...";

//create remote input that will read text
var remoteInput = new Android.Support.V4.App.RemoteInput.Builder(KEY_TEXT_REPLY)
						        .SetLabel(replyText)
                                                        .Build();

After we have the RemoteInput we can create a new action and attach it to it a new action:


var action = new NotificationCompat.Action.Builder(Resource.Drawable.action_reply,
                                                   replyText,
                                                   pendingIntent)											          
                                                  .AddRemoteInput(remoteInput)
                                                  .Build();

Build and Send Notification

With our action with remote input created, it’s finally time to send the notification.

var notification = new NotificationCompat.Builder(this)
					 .SetSmallIcon(Resource.Drawable.reply)
					 .SetLargeIcon(BitmapFactory.DecodeResource(Resources, Resource.Drawable.avatar))
					 .SetContentText("Hey, it is James! What's up?")
					 .SetContentTitle("Message")
					 .SetAutoCancel(true)
                                         .AddAction(action)
					 .Build();

using (var notificationManager = NotificationManagerCompat.From(this))
{
	notificationManager.Notify(requestCode, notification);
}

Now our notification is live with the remote input visible: notification

Processing Input

When the user inputs text into the direct reply, we’re able to retrieve the text from the Intent that is passed in with just a few lines of code:

var remoteInput = RemoteInput.GetResultsFromIntent(Intent);
var reply = remoteInput?.GetCharSequence(MainActivity.KEY_TEXT_REPLY) ?? string.Empty;

This should be done in a background service or broadcast receiver with the “com.xamarin.directreply.REPLY” Intent Filter specified.

Here’s our final BroadcastReceiver that will pop up a toast message and will update the notification to stop the progress indicator in the notification:

[BroadcastReceiver(Enabled = true)]
[Android.App.IntentFilter(new[] { MainActivity.REPLY_ACTION })]
/// 
/// A receiver that gets called when a reply is sent
/// 
public class MessageReplyReceiver : BroadcastReceiver
{
	public override void OnReceive(Context context, Intent intent)
	{
		if (!MainActivity.REPLY_ACTION.Equals(intent.Action))
			return;
		

		int requestId = intent.GetIntExtra(MainActivity.REQUEST_CODE, -1);
		if (requestId == -1)
			return;

		var reply = GetMessageText(intent);
		using (var notificationManager = NotificationManagerCompat.From(context))
		{
			//Create new notification to display, or re-build existing conversation to update with new response
			var notificationBuilder = new NotificationCompat.Builder(context);
			notificationBuilder.SetSmallIcon(Resource.Drawable.reply);
			notificationBuilder.SetContentText("Replied");
			var repliedNotification = notificationBuilder.Build();


			//Call notify to stop progress spinner. 
			notificationManager.Notify(requestId, repliedNotification);
		}

		Toast.MakeText(context, $"Message sent: {reply}", ToastLength.Long).Show();
	}

	/// 
	/// Get the message text from the intent.
	/// Note that you should call  
	/// to process the RemoteInput.
	/// 
	/// The message text.
	/// Intent.
	static string GetMessageText(Intent intent)
	{
		var remoteInput = RemoteInput.GetResultsFromIntent(intent);
		return remoteInput?.GetCharSequence(MainActivity.KEY_TEXT_REPLY) ?? string.Empty;
	}
}

Learn More

To learn more about the great new features in Android N, including Notification enhancements, be sure to read our full Android N Getting Started Guide. You can find a full example of Direct Reply and other notification enhancements in our Samples Gallery.

0 comments

Discussion is closed.

Feedback usabilla icon