Saturday 15 November 2014

Enhanced and Wearable-Ready Notifications on Android

Introduction

Notifications are a very useful way to interact with your application's users and, with Android Wear, we now also have wearable devices running Android. It's therefore a good idea to learn how to take advantage of these new features by adding appropriate actions to notifications or by creating actions that are only visible on wearable devices.

In this tutorial, I'm going to show you the modern implementation of notifications, as shown during this year's Google I/O. We'll use the new support package and extend its capabilities by adding actions that are only visible on smartwatches, the only wearable devices available with Android Wear at the time of writing.

1. Prerequisites

For this project, you can use either Android Studio or the Android Developer Tools. If you're using Android Studio, then make sure to add the following line to your build.gradle file.

1
compile "com.android.support:support-v4:20.0.+"
2. Setting Up the Project

Launch your IDE and create a new Android project, or open a project you've created previously. For this tutorial, I'm going to create a new project and name it ImprovedNotifications. Don't forget to use a unique package name.

While setting up the project, make sure that you select the Empty Activity option in the Create Activity step.

Once the project is created, create a new Activity, ActivatedActivity. This Activity is going to be called from a notification on your mobile or wearable device.

Before we move on, we need to update the strings.xml file by adding the strings that we're going to be using a bit later in this tutorial

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="app_name">ImprovedNotifications</string>
    <string name="title_activity_activated">ActivatedActivity</string>
   
    <string name="message">Hi"!" I"'"m the activated activity</string>
    <string name="button_text">Try me for a new notification</string>
   
    <string name="notification_title">Hey Mom"!" I"'""m a title</string>
    <string name="notification_text">Look at me"!" I"'"m a sexy notification content</string>
    <string name="first_action">Let"'"s see the author"'"s twitter profile</string>
   
    <string name="wearable_action">I only appear here</string>
</resources>
3. Creating the Layout

The next step is to create a layout for the MainActivity and the ActivatedActivity classes. The layout for the MainActivity class is shown below.

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="${relativePackage}.${activityClass}" >
    <Button
        android:id="@+id/notification_button"
        android:layout_height="wrap_content"
        android:layout_width="wrap_content"
        android:layout_centerInParent="true"
        android:padding="10dp"
        android:text="@string/button_text"/>
</RelativeLayout>
And this is the layout for the ActivatedActivity class.

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="${relativePackage}.${activityClass}" >
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:padding="10dp"
        android:layout_centerInParent="true"
        android:text="@string/message"/>
</RelativeLayout>
4. Creating a Notification

We create a notification in the MainActivity class. In the code snippet below, you can see what steps are involved in creating a notification. I've commented the code block to help you understand the various steps, but let's walk through the snippet step by step.

package com.androiheroes.improvednotifications;

import android.app.Activity;
import android.app.Notification;
import android.app.PendingIntent;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.support.v4.app.NotificationCompat;
import android.support.v4.app.NotificationManagerCompat;
import android.view.View;
import android.widget.Button;

public class MainActivity extends Activity {

    /* Widgets you are going to use */
  private Button button;
  
  /*
   * This is the notification id
   * You can use it to dismiss the notification calling the .cancel() method on the notification_manager object
   */
  private int notification_id = 1;
  private final String NOTIFICATION_ID = "notification_id";
  
  /* These are the classes you use to start the notification */
  private NotificationCompat.Builder notification_builder;
  private NotificationManagerCompat notification_manager;
  
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    
    /*
     * Step 1
     * Instantiation of the button you use to start the notification
     */
    button = (Button) findViewById(R.id.notification_button);
    
    /*
     * Step 2
     * Create the intent you are going to launch when your notification is pressed
     * and let the PendingIntent handle it
     */
    Intent open_activity_intent = new Intent(this, ActivatedActivity.class);
    open_activity_intent.putExtra(NOTIFICATION_ID, notification_id);
    PendingIntent pending_intent = PendingIntent.getActivity(this, 0, open_activity_intent, PendingIntent.FLAG_CANCEL_CURRENT);
        
    /*
     * Step 3
     * Here you create the notification and start adding all the attributes you are going to use
     */
    notification_builder = new NotificationCompat.Builder(this)
      .setSmallIcon(R.drawable.ic_launcher)
      .setContentTitle(getString(R.string.notification_title))
      .setContentText(getString(R.string.notification_text))
      /*
       * This method specifies that our notification must have all the default characteristics of a notification
       * like sound and vibration
       */
      .setDefaults(Notification.DEFAULT_ALL)
      /* This method is going to dismiss the notification once it is pressed */
      .setAutoCancel(true)
      .setContentIntent(pending_intent);
    
    /*
     * Step 4
     * Here we instantiate the Notification Manager object to start/stop the notifications
     */
    notification_manager = NotificationManagerCompat.from(this);
  }
  
  @Override
  protected void onStart() {
    super.onStart();
    
    /*
     * Step 5
     * The notification is going to appear when you press the button on the screen
     */
    button.setOnClickListener(new View.OnClickListener() {
      @Override
      public void onClick(View v) {
        notification_manager.notify(notification_id, notification_builder.build());
      }
    });
  }
}

Step 1
We first instantiate the button that we'll use to launch the notification. You could also create the notification directly in the onCreate method, but by using a button you have more control over the exact timing of the notification.

Step 2
In the second step, we instantiate an Intent object with the task to perform when the notification is tapped. We pass the object to a PendingIntent instance to handle it later when it's called.

Step 3
Using the Android Support Library, we create the notification using the Builder class of the NotificationCompat object and set its attributes.

Step 4
In this step, we instantiate a NotificationManagerCompat instance to start and/or stop the notification anytime we want. This will make testing much easier.

Step 5
When the button is tapped, the notification is fired using the notify method.

Don't forget to use the classes from the Android Support Library. This way you can be sure your notification is going to look fine on older versions of Android.

You can now run the app, tap the button, and see the notification appear at the top of the screen. If you tap the notification, it should take you to the ActivatedActivity activity. With the notification set up and working, it's time to start adding actions to it.

5. Adding Actions to the Notification

You can add extra actions to the notification by invoking the addAction method on the notification_builder object. For this to work, you need to pass a PendingIntent instance with the task you like to perform.

In the following code snippet, I show you the steps you have to implement to create an action with a custom task. In this example, I'm going to take you to my Twitter profile in the Twitter app. This means I need a URI instance pointing to my Twitter profile, add this to the Intent, and let the PendingIntent handle it when the action is tapped. Insert this code block before the instantiation of the notification_builder object.

/* The action in the handheld notification must perform some task
* In this case the author's twitter profile is going to be opened, in the twitter app, when it is clicked
* but you can change it with your profile if you want ;)
*/
Intent open_twitter_profile = new Intent(Intent.ACTION_VIEW);
Uri twitter_profile_location = Uri.parse("twitter://user?screen_name=@kerpie");
open_twitter_profile.setData(twitter_profile_location);
PendingIntent twitter_intent = PendingIntent.getActivity(this, 0, open_twitter_profile, 0);
To add the action, invoke the addAction method on the notification_builder object and pass in the open_twitter_profile object we just created.

/*
* Here you create the notification and start adding all the attributes
* you are going to use
*/
         
notification_builder = new NotificationCompat.Builder(this)
    .setSmallIcon(R.drawable.ic_launcher)
    .setContentTitle(getString(R.string.notification_title))
    .setContentText(getString(R.string.notification_text))
    /*
     * This method specifies that your notification must have all the default characteristics of a notification
     * like sound and vibration
     */        
    .setDefaults(Notification.DEFAULT_ALL)
    /* This method is going to dismiss the notification once it is pressed */
    .setAutoCancel(true)
    .setContentIntent(pending_intent)
    /*
     * Here you can add actions to your handheld device
     * just take care of the quantity of actions you add
     * in this case, like in many others, less is more
     */
    .addAction(android.R.drawable.ic_dialog_info, getString(R.string.first_action), twitter_intent);
Run the application, tap the button to trigger the notification, and you should see the notification appear along with the action we just created.

While you can add more actions to a notification using the addAction method, make sure the user isn't overwhelmed by the number of actions they can choose from.

6. Supporting Android Wear

So far, we have used the classes from the Android Support Library to make sure the notifications are also shown on smartwatches running Android Wear. You can run the application on a physical smartwatch or you can try it on the emulator from the Android Virtual Device Manager. Either way, you need to sync your device with the smartwatch.

Before syncing your device with the smartwatch emulator, you need to install the Android Wear app, which is available on Google Play. After you've unplugged every other Android device connected to your computer, execute the following command from the command line.

adb devices

This command lists the devices connected to your development machine. You should see two of them, the smartwatch emulator and your device. Then run the following command from the command line to enable port forwarding.

adb -d forward tcp:5601 tcp:5601

You can now connect your device with the emulator and turn on the notifications using the Android Wear app. Run the app again and trigger the notification. The notification should look similar to the one shown below.

Advertisement
7. Adding Wearable-Only Actions

It is possible to add actions that are only visible on wearables. This is accomplished by invoking the addAction method of the WearableExtender class. The result is that any actions added through the NotificationCompat.Builder class are ignored.

As we did before, to trigger the action, we make use of an Intent and a PendingIntent instance, but we'll create the action displayed on the wearable device using the Builder class of a special Action class, which is part of the NotificationCompat class as shown below.

/* Here we instantiate the Intent we want to use when the action in the smartwatch is pressed */
Intent wearable_intent = new Intent(this, ActivatedActivity.class);
PendingIntent wearable_pending_intent = PendingIntent.getActivity(this, 0, wearable_intent, PendingIntent.FLAG_UPDATE_CURRENT);

/* Now we have an intent for the wearable created we have to create a wearable action using it*/
NotificationCompat.Action wearable_action = new NotificationCompat.Action.Builder(
        android.R.drawable.ic_dialog_email,
        getString(R.string.wearable_action),
        wearable_pending_intent).build();
We then add this action to the notification_builder object using the extend method as shown below.

/*
* Here you create the notification and start adding all the attributes
* you are going to use
*/
         
notification_builder = new NotificationCompat.Builder(this)
    .setSmallIcon(R.drawable.ic_launcher)
    .setContentTitle(getString(R.string.notification_title))
    .setContentText(getString(R.string.notification_text))
    /*
     * This method specifies that your notification must have all the default characteristics of a notification
     * like sound and vibration
     */        
    .setDefaults(Notification.DEFAULT_ALL)
    /* This method is going to dismiss the notification once it is pressed */
    .setAutoCancel(true)
    .setContentIntent(pending_intent)
    /*
     * Here you can add actions to your handheld device
     * just take care of the quantity of actions you add
     * in this case, like in many others, less is more
     */
    .addAction(android.R.drawable.ic_dialog_info, getString(R.string.first_action), twitter_intent)
    /*
     * Here you add a wearable-only action
     * This action won't be visible in the handheld device
     */
    .extend(new WearableExtender().addAction(wearable_action));
Run the app and tap the button to display the notification on your device. It should be different than the notification that pops up on the wearable emulator.

Conclusion

Smartwatches are here to stay, for a while at least, and it's therefore important to take advantage of this new way of communicating with your application's users. I hope you've found the tutorial helpful and don't forget to share it if you liked it.

4 comments:

  1. Are you looking for free Google+ Circles?
    Did you know that you can get them ON AUTOPILOT & TOTALLY FREE by getting an account on Like 4 Like?

    ReplyDelete
  2. Thanks for one marvelous posting! I enjoyed reading it; you are a great author.I will make sure to bookmark your blog and may come back someday. I want to encourage that you continue your great posts, have a nice weekend!

    https://certificationanswers.club

    ReplyDelete
  3. I essentially needed to thank you so much once more. I don't know the things that I may have experienced without the kind of clues uncovered by you in regards to that circumstance.google ads display certification

    ReplyDelete