打开 res/layout/activity_main.xml 文件,替换以下内容定义:
它会添加新的 EditText 控件,方便你以用户身份登录。 此外还会添加一个字段用作要发送的通知的一部分的用户名标记:
xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
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" />
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" />
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" />
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" />
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" />
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" />
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" />
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" />
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" />
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
android:id="@+id/text_hello"
/>
打开 res/values/strings.xml 文件并将 send_button 定义替换为以下行,以重新定义 send_button 的字符串并为其他控件添加字符串:
Username
Password
1. Sign in
2. Send Notification
Notification message
Recipient username
main_activity.xml 的图形布局现在应如下图所示:
在 MainActivity 类所在的包中创建一个名为 RegisterClient 的新类。 将以下代码用于新的类文件。
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 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;
}
}
此组件将实现所需的 REST 调用,以便能够联系应用后端来注册推送通知。 它还会在本地存储通知中心创建的 registrationIds,如 从应用后端注册中所述。 它使用你在单击“登录”按钮时存储在本地存储中的授权令牌。
在 MainActivity 类中,添加一个用于 RegisterClient 类的字段和一个用于 ASP.NET 后端终结点的字符串。 确保使用前面获取的实际后端终结点来替换 。 例如,http://mybackend.azurewebsites.net。
private RegisterClient registerClient;
private static final String BACKEND_ENDPOINT = "";
FirebaseInstanceId fcm;
String FCM_token = null;
在 MainActivity 类的 onCreate 方法中,删除或注释掉 hub 字段的初始化以及对 registerWithNotificationHubs 方法的调用。 然后,添加代码来初始化 RegisterClient 类的实例。 该方法应包含以下行:
@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);
}
向 MainActivity.java 文件添加以下 import 语句。
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;
将 onStart 方法的代码替换为以下代码:
super.onStart();
Button sendPush = (Button) findViewById(R.id.sendbutton);
sendPush.setEnabled(false);
然后,添加以下方法,处理“登录”按钮的单击事件,并发送推送通知。
public void login(View view) throws UnsupportedEncodingException {
this.registerClient.setAuthorizationHeader(getAuthorizationHeader());
final Context context = this;
new AsyncTask() {
@Override
protected Object doInBackground(Object... params) {
try {
FirebaseInstanceId.getInstance().getInstanceId().addOnSuccessListener(new OnSuccessListener() {
@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());
} 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() {
@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);
}
“登录”按钮的 login 处理程序生成在输入的用户名和密码上使用的基本身份验证令牌(代表身份验证方案使用的任何令牌),然后使用 RegisterClient 调用后端来注册。
sendPush 方法调用后端来触发根据用户标记向用户发送安全通知。 sendPush 针对的平台通知服务取决于传入的 pns 字符串。
将下面的 DialogNotify 方法添加到 MainActivity 类。
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();
}
在 MainActivity 类中,更新 sendNotificationButtonOnClick 方法以使用用户选择的平台通知服务调用 sendPush 方法,如下所示。
/**
* 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);
}
}
在 build.gradle 文件中,将以下行添加到 buildTypes 节后的 android 节。
useLibrary 'org.apache.http.legacy'
如果你的应用面向 API 级别 28 (Android 9.0) 或更高版本,请在 AndroidManifest.xml 的 元素中包含以下声明。
android:name="org.apache.http.legacy"
android:required="false" />
生成项目。