Raw New Markdown
Generating updated version of doc...
Rendered New Markdown
Generating updated version of doc...
---
title: Send push notifications to specific Android apps using Azure Notification Hubs
description: Learn how to send push notifications to specific Android apps by using Azure Notification Hubs.
services: notification-hubs
author: sethmanheim
manager: lizross
ms.service: azure-notification-hubs
ms.tgt_pltfrm: mobile-android
ms.devlang: java
ms.topic: tutorial
ms.custom: mvc, devx-track-java, devx-track-dotnet
ms.date: 02/06/2024
ms.author: sethm
ms.reviewer: heathertian
ms.lastreviewed: 02/06/2024
---
# Tutorial: Send push notifications to specific Android apps using Azure Notification Hubs
[!INCLUDE [notification-hubs-selector-aspnet-backend-notify-users](../../includes/notification-hubs-selector-aspnet-backend-notify-users.md)]
> [!NOTE]
> For information about Firebase Cloud Messaging deprecation and migration steps, see [Google Firebase Cloud Messaging migration](notification-hubs-gcm-to-fcm.md).
This tutorial shows you how to use Azure Notification Hubs to send push notifications to a specific app user on a specific device. An ASP.NET WebAPI backend is used to authenticate clients and to generate notifications, as shown in the guidance article [Registering from your app backend](notification-hubs-push-notification-registration-management.md#registration-management-from-a-backend). This tutorial builds on the notification hub that you created in the [Tutorial: Push notifications to Android devices by using Azure Notification Hubs and Firebase Cloud Messaging](notification-hubs-android-push-notification-google-fcm-get-started.md).
In this tutorial, you take the following steps:
> [!div class="checklist"]
> * Create the backend Web API project that authenticates users.
> * Update the Android application.
> * Test the app
## Prerequisites
Complete the [Tutorial: Push notifications to Android devices by using Azure Notification Hubs and Firebase Cloud Messaging](notification-hubs-android-push-notification-google-fcm-get-started.md) before doing this tutorial.
[!INCLUDE [notification-hubs-aspnet-backend-notifyusers](../../includes/notification-hubs-aspnet-backend-notifyusers.md)]
## Create the Android Project
The next step is to update the Android application created in the [Tutorial: Push notifications to Android devices by using Azure Notification Hubs and Firebase Cloud Messaging](notification-hubs-android-push-notification-google-fcm-get-started.md).
1. Open your `res/layout/activity_main.xml` file, replace the following content definitions:
It adds new EditText controls for logging in as a user. Also a field is added for a username tag that will be part of notifications you send:
```xml
<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=".MainActivity">
<EditText
android:id="@+id/usernameText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ems="10"
android:hint="@string/usernameHint"
android:layout_above="@+id/passwordText"
android:layout_alignParentEnd="true" />
<EditText
android:id="@+id/passwordText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ems="10"
android:hint="@string/passwordHint"
android:inputType="textPassword"
android:layout_above="@+id/buttonLogin"
android:layout_alignParentEnd="true" />
<Button
android:id="@+id/buttonLogin"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/loginButton"
android:onClick="login"
android:layout_above="@+id/toggleButtonFCM"
android:layout_centerHorizontal="true"
android:layout_marginBottom="24dp" />
<ToggleButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textOn="WNS on"
android:textOff="WNS off"
android:id="@+id/toggleButtonWNS"
android:layout_toLeftOf="@id/toggleButtonFCM"
android:layout_centerVertical="true" />
<ToggleButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textOn="FCM on"
android:textOff="FCM off"
android:id="@+id/toggleButtonFCM"
android:checked="true"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true" />
<ToggleButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textOn="APNS on"
android:textOff="APNS off"
android:id="@+id/toggleButtonAPNS"
android:layout_toRightOf="@id/toggleButtonFCM"
android:layout_centerVertical="true" />
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/editTextNotificationMessageTag"
android:layout_below="@id/toggleButtonFCM"
android:layout_centerHorizontal="true"
android:hint="@string/notification_message_tag_hint" />
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/editTextNotificationMessage"
android:layout_below="@+id/editTextNotificationMessageTag"
android:layout_centerHorizontal="true"
android:hint="@string/notification_message_hint" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/send_button"
android:id="@+id/sendbutton"
android:onClick="sendNotificationButtonOnClick"
android:layout_below="@+id/editTextNotificationMessage"
android:layout_centerHorizontal="true" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
android:id="@+id/text_hello"
/>
</RelativeLayout>
```
2. Open your `res/values/strings.xml` file and replace the `send_button` definition with the following lines that redefine the string for the `send_button` and add strings for the other controls:
```xml
<string name="usernameHint">Username</string>
<string name="passwordHint">Password</string>
<string name="loginButton">1. Sign in</string>
<string name="send_button">2. Send Notification</string>
<string name="notification_message_hint">Notification message</string>
<string name="notification_message_tag_hint">Recipient username</string>
```
Your `main_activity.xml` graphical layout should now look like the following image:
![Screenshot of an emulator displaying what the main activity X M L graphical layout will look like.][A1]
3. Create a new class named `RegisterClient` in the same package as your `MainActivity` class. Use the code below for the new class file.
```java
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.Set;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.util.EntityUtils;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import android.content.Context;
import android.content.SharedPreferences;
import android.util.Log;
public class RegisterClient {
private static final String PREFS_NAME = "ANHSettings";
private static final String REGID_SETTING_NAME = "ANHRegistrationId";
private String Backend_Endpoint;
SharedPreferences settings;
protected HttpClient httpClient;
private String authorizationHeader;
public RegisterClient(Context context, String backendEndpoint) {
super();
this.settings = context.getSharedPreferences(PREFS_NAME, 0);
httpClient = new DefaultHttpClient();
Backend_Endpoint = backendEndpoint + "/api/register";
}
public String getAuthorizationHeader() {
return authorizationHeader;
}
public void setAuthorizationHeader(String authorizationHeader) {
this.authorizationHeader = authorizationHeader;
}
public void register(String handle, Set<String> tags) throws ClientProtocolException, IOException, JSONException {
String registrationId = retrieveRegistrationIdOrRequestNewOne(handle);
JSONObject deviceInfo = new JSONObject();
deviceInfo.put("Platform", "fcm");
deviceInfo.put("Handle", handle);
deviceInfo.put("Tags", new JSONArray(tags));
int statusCode = upsertRegistration(registrationId, deviceInfo);
if (statusCode == HttpStatus.SC_OK) {
return;
} else if (statusCode == HttpStatus.SC_GONE){
settings.edit().remove(REGID_SETTING_NAME).commit();
registrationId = retrieveRegistrationIdOrRequestNewOne(handle);
statusCode = upsertRegistration(registrationId, deviceInfo);
if (statusCode != HttpStatus.SC_OK) {
Log.e("RegisterClient", "Error upserting registration: " + statusCode);
throw new RuntimeException("Error upserting registration");
}
} else {
Log.e("RegisterClient", "Error upserting registration: " + statusCode);
throw new RuntimeException("Error upserting registration");
}
}
private int upsertRegistration(String registrationId, JSONObject deviceInfo)
throws UnsupportedEncodingException, IOException,
ClientProtocolException {
HttpPut request = new HttpPut(Backend_Endpoint+"/"+registrationId);
request.setEntity(new StringEntity(deviceInfo.toString()));
request.addHeader("Authorization", "Basic "+authorizationHeader);
request.addHeader("Content-Type", "application/json");
HttpResponse response = httpClient.execute(request);
int statusCode = response.getStatusLine().getStatusCode();
return statusCode;
}
private String retrieveRegistrationIdOrRequestNewOne(String handle) throws ClientProtocolException, IOException {
if (settings.contains(REGID_SETTING_NAME))
return settings.getString(REGID_SETTING_NAME, null);
HttpUriRequest request = new HttpPost(Backend_Endpoint+"?handle="+handle);
request.addHeader("Authorization", "Basic "+authorizationHeader);
HttpResponse response = httpClient.execute(request);
if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {
Log.e("RegisterClient", "Error creating registrationId: " + response.getStatusLine().getStatusCode());
throw new RuntimeException("Error creating Notification Hubs registrationId");
}
String registrationId = EntityUtils.toString(response.getEntity());
registrationId = registrationId.substring(1, registrationId.length()-1);
settings.edit().putString(REGID_SETTING_NAME, registrationId).commit();
return registrationId;
}
}
```
This component implements the REST calls required to contact the app backend to register for push notifications. It also locally stores the *registrationIds* created by the Notification Hub as detailed in [Registering from your app backend](notification-hubs-push-notification-registration-management.md#registration-management-from-a-backend). It uses an authorization token stored in local storage when you click the **Sign in** button.
4. In your `MainActivity` class, and add a field for the `RegisterClient` class and a string for your ASP.NET backend's endpoint. Be sure to replace `<Enter Your Backend Endpoint>` with your actual backend endpoint obtained previously. For example, `http://mybackend.azurewebsites.net`.
```java
private RegisterClient registerClient;
private static final String BACKEND_ENDPOINT = "<Enter Your Backend Endpoint>";
FirebaseInstanceId fcm;
String FCM_token = null;
```
5. In your `MainActivity` class, in the `onCreate` method, remove, or comment out the initialization of the `hub` field and the call to the `registerWithNotificationHubs` method. Then add code to initialize an instance of the `RegisterClient` class. The method should contain the following lines:
```java
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mainActivity = this;
FirebaseService.createChannelAndHandleNotifications(getApplicationContext());
fcm = FirebaseInstanceId.getInstance();
registerClient = new RegisterClient(this, BACKEND_ENDPOINT);
setContentView(R.layout.activity_main);
}
```
7. Add the following `import` statements to your `MainActivity.java` file.
```java
import android.util.Base64;
import android.view.View;
import android.widget.EditText;
import android.widget.Button;
import android.widget.ToggleButton;
import java.io.UnsupportedEncodingException;
import android.content.Context;
import java.util.HashSet;
import android.widget.Toast;
import org.apache.http.client.ClientProtocolException;
import java.io.IOException;
import org.apache.http.HttpStatus;
import android.os.AsyncTask;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import android.app.AlertDialog;
import android.content.DialogInterface;
import com.google.firebase.iid.FirebaseInstanceId;
import com.google.firebase.iid.InstanceIdResult;
import com.google.android.gms.tasks.OnSuccessListener;
import java.util.concurrent.TimeUnit;
```
8. Replace code on the onStart method with the following code:
```java
super.onStart();
Button sendPush = (Button) findViewById(R.id.sendbutton);
sendPush.setEnabled(false);
```
9. Then, add the following methods to handle the **Sign in** button click event and sending push notifications.
```java
public void login(View view) throws UnsupportedEncodingException {
this.registerClient.setAuthorizationHeader(getAuthorizationHeader());
final Context context = this;
new AsyncTask<Object, Object, Object>() {
@Override
protected Object doInBackground(Object... params) {
try {
FirebaseInstanceId.getInstance().getInstanceId().addOnSuccessListener(new OnSuccessListener<InstanceIdResult>() {
@Override
public void onSuccess(InstanceIdResult instanceIdResult) {
FCM_token = instanceIdResult.getToken();
Log.d(TAG, "FCM Registration Token: " + FCM_token);
}
});
TimeUnit.SECONDS.sleep(1);
registerClient.register(FCM_token, new HashSet<String>());
} catch (Exception e) {
DialogNotify("MainActivity - Failed to register", e.getMessage());
return e;
}
return null;
}
protected void onPostExecute(Object result) {
Button sendPush = (Button) findViewById(R.id.sendbutton);
sendPush.setEnabled(true);
Toast.makeText(context, "Signed in and registered.",
Toast.LENGTH_LONG).show();
}
}.execute(null, null, null);
}
private String getAuthorizationHeader() throws UnsupportedEncodingException {
EditText username = (EditText) findViewById(R.id.usernameText);
EditText password = (EditText) findViewById(R.id.passwordText);
String basicAuthHeader = username.getText().toString()+":"+password.getText().toString();
basicAuthHeader = Base64.encodeToString(basicAuthHeader.getBytes("UTF-8"), Base64.NO_WRAP);
return basicAuthHeader;
}
/**
* This method calls the ASP.NET WebAPI backend to send the notification message
* to the platform notification service based on the pns parameter.
*
* @param pns The platform notification service to send the notification message to. Must
* be one of the following ("wns", "fcm", "apns").
* @param userTag The tag for the user who will receive the notification message. This string
* must not contain spaces or special characters.
* @param message The notification message string. This string must include the double quotes
* to be used as JSON content.
*/
public void sendPush(final String pns, final String userTag, final String message)
throws ClientProtocolException, IOException {
new AsyncTask<Object, Object, Object>() {
@Override
protected Object doInBackground(Object... params) {
try {
String uri = BACKEND_ENDPOINT + "/api/notifications";
uri += "?pns=" + pns;
uri += "&to_tag=" + userTag;
HttpPost request = new HttpPost(uri);
request.addHeader("Authorization", "Basic "+ getAuthorizationHeader());
request.setEntity(new StringEntity(message));
request.addHeader("Content-Type", "application/json");
HttpResponse response = new DefaultHttpClient().execute(request);
if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {
DialogNotify("MainActivity - Error sending " + pns + " notification",
response.getStatusLine().toString());
throw new RuntimeException("Error sending notification");
}
} catch (Exception e) {
DialogNotify("MainActivity - Failed to send " + pns + " notification ", e.getMessage());
return e;
}
return null;
}
}.execute(null, null, null);
}
```
The `login` handler for the **Sign in** button generates a basic authentication token using on the input username and password (it represents any token your authentication scheme uses), then it uses `RegisterClient` to call the backend for registration.
The `sendPush` method calls the backend to trigger a secure notification to the user based on the user tag. The platform notification service that `sendPush` targets depends on the `pns` string passed in.
10. Add the following `DialogNotify` method to the `MainActivity` class.
```java
protected void DialogNotify(String title, String message)
{
AlertDialog alertDialog = new AlertDialog.Builder(MainActivity.this).create();
alertDialog.setTitle(title);
alertDialog.setMessage(message);
alertDialog.setButton(AlertDialog.BUTTON_NEUTRAL, "OK",
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
}
});
alertDialog.show();
}
```
11. In your `MainActivity` class, update the `sendNotificationButtonOnClick` method to call the `sendPush` method with the user's selected platform notification services as follows.
```java
/**
* Send Notification button click handler. This method sends the push notification
* message to each platform selected.
*
* @param v The view
*/
public void sendNotificationButtonOnClick(View v)
throws ClientProtocolException, IOException {
String nhMessageTag = ((EditText) findViewById(R.id.editTextNotificationMessageTag))
.getText().toString();
String nhMessage = ((EditText) findViewById(R.id.editTextNotificationMessage))
.getText().toString();
// JSON String
nhMessage = "\"" + nhMessage + "\"";
if (((ToggleButton)findViewById(R.id.toggleButtonWNS)).isChecked())
{
sendPush("wns", nhMessageTag, nhMessage);
}
if (((ToggleButton)findViewById(R.id.toggleButtonFCM)).isChecked())
{
sendPush("fcm", nhMessageTag, nhMessage);
}
if (((ToggleButton)findViewById(R.id.toggleButtonAPNS)).isChecked())
{
sendPush("apns", nhMessageTag, nhMessage);
}
}
```
12. In the `build.gradle` file, add the following line to the `android` section after the `buildTypes` section.
```java
useLibrary 'org.apache.http.legacy'
```
13. If your app is targeting API level 28 (Android 9.0) or above, include the following declaration within the `<application>` element of `AndroidManifest.xml`.
```xml
<uses-library
android:name="org.apache.http.legacy"
android:required="false" />
```
14. Build the project.
## Test the app
1. Run the application on a device or an emulator using Android Studio.
2. In the Android app, enter a username and password. They must both be the same string value and they must not contain spaces or special characters.
3. In the Android app, click **Sign in**. Wait for a toast message that states **Signed in and registered**. It enables the **Send Notification** button.
![Screenshot of an emulator showing what the Notification Hubs Notify Users app looks like after logging in.][A2]
4. Click the toggle buttons to enable all platforms where you ran the app and registered a user.
5. Enter the user's name that receives the notification message. That user must be registered for notifications on the target devices.
6. Enter a message for the user to receive as a push notification message.
7. Click **Send Notification**. Each device that has a registration with the matching username tag receives the push notification.
## Next steps
In this tutorial, you learned how to push notifications to specific users that have tags associated with their registrations. To learn how to push location-based notifications, advance to the following tutorial:
> [!div class="nextstepaction"]
>[Push location-based notifications](notification-hubs-push-bing-spatial-data-geofencing-notification.md)
[A1]: ./media/notification-hubs-aspnet-backend-android-notify-users/android-notify-users.png
[A2]: ./media/notification-hubs-aspnet-backend-android-notify-users/android-notify-users-enter-password.png