Android通知直接回复
Android Notification Direct Reply操作可让我们回复通知消息,它在Whatsapp和Facebook Messenger通知消息等聊天通知中非常流行。
Android Nougat引入了一些新功能。
它提供了一些很棒的功能,例如内联回复操作和捆绑通知。
在本教程中,我们将在应用程序中实现内联回复
Android通知直接回复
内联回复操作(也称为直接回复)使我们能够从通知本身中回复消息。
通过消除打开用于提供输入的应用程序的需求,它使生活变得更轻松。
这些功能通常在消息传递应用程序中看到。
直接答复结合使用了通知操作和远程输入。
远程输入API提供了一种从我们的应用程序中的通知中访问输入文本的机制。
RemoteInput需要以下字符串作为输入。
唯一键:此键用于正确识别以后从内联通知中输入的文本。
标签:这作为提示文本显示给用户。
让我们实现一个基本应用程序,该应用程序会触发一个通知,并将"内联回复"设置为"操作"。
Android通知直接回复代码
下面给出了" activity_main.xml"布局的代码。
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="https://schemas.android.com/apk/res/android"
xmlns:app="https://schemas.android.com/apk/res-auto"
xmlns:tools="https://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.theitroad.directreplynotification.MainActivity">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="BASIC INLINE REPLY NOTIFICATION"
android:id="@+id/btn_basic_inline_reply"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
<TextView
android:id="@+id/txt_inline_reply"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Replied text will be displayed here"
android:layout_marginTop="8dp"
app:layout_constraintTop_toBottomOf="@+id/btn_basic_inline_reply"
android:layout_marginLeft="8dp"
app:layout_constraintLeft_toLeftOf="parent"
android:layout_marginRight="8dp"
app:layout_constraintRight_toRightOf="parent"
<Button
android:id="@+id/btn_inline_replies_with_history"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="INLINE REPLIES WITH HISTORY"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
android:layout_marginTop="8dp"
app:layout_constraintTop_toBottomOf="@+id/txt_inline_reply"
</android.support.constraint.ConstraintLayout>
注意:我们将在本教程的后面部分介绍第二个按钮。
下面给出了MainActivity.java类的代码。
package com.theitroad.directreplynotification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.support.v4.app.NotificationCompat;
import android.support.v4.app.RemoteInput;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
String KEY_REPLY = "key_reply";
public static final int NOTIFICATION_ID = 1;
Button btnBasicInlineReply;
TextView txtReplied;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
clearExistingNotifications();
btnBasicInlineReply = (Button) findViewById(R.id.btn_basic_inline_reply);
txtReplied = (TextView) findViewById(R.id.txt_inline_reply);
btnBasicInlineReply.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn_basic_inline_reply:
//Create notification builder
NotificationCompat.Builder builder = new NotificationCompat.Builder(this)
.setSmallIcon(android.R.drawable.stat_notify_chat)
.setContentTitle("Inline Reply Notification");
String replyLabel = "Enter your reply here";
//Initialise RemoteInput
RemoteInput remoteInput = new RemoteInput.Builder(KEY_REPLY)
.setLabel(replyLabel)
.build();
//PendingIntent that restarts the current activity instance.
Intent resultIntent = new Intent(this, MainActivity.class);
resultIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
PendingIntent resultPendingIntent = PendingIntent.getActivity(this, 0, resultIntent, PendingIntent.FLAG_UPDATE_CURRENT);
//Notification Action with RemoteInput instance added.
NotificationCompat.Action replyAction = new NotificationCompat.Action.Builder(
android.R.drawable.sym_action_chat, "REPLY", resultPendingIntent)
.addRemoteInput(remoteInput)
.setAllowGeneratedReplies(true)
.build();
//Notification.Action instance added to Notification Builder.
builder.addAction(replyAction);
Intent intent = new Intent(this, MainActivity.class);
intent.putExtra("notificationId", NOTIFICATION_ID);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
PendingIntent dismissIntent = PendingIntent.getActivity(getBaseContext(), 0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
builder.addAction(android.R.drawable.ic_menu_close_clear_cancel, "DISMISS", dismissIntent);
//Create Notification.
NotificationManager notificationManager =
(NotificationManager)
getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(NOTIFICATION_ID,
builder.build());
break;
}
}
private void clearExistingNotifications()
{
int notificationId = getIntent().getIntExtra("notificationId", 0);
NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
manager.cancel(notificationId);
}
}
在通知上设置了两个通知动作,即" REPLY"和" DISMISS"。
单击REPLY将触发直接答复功能。
在模拟器上运行以上代码将得到以下输出。
在上面的gif中,单击通知中的发送图标会显示一个指示器。
这意味着通知正在等待活动的确认。
我们尚未在活动中处理回复的文本。
内联回复文本是使用RemoteInput实例中的键集检索的,如下面的MainActivity.java的更新代码所示。
package com.theitroad.directreplynotification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.support.v4.app.NotificationCompat;
import android.support.v4.app.RemoteInput;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import java.util.Random;
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
String KEY_REPLY = "key_reply";
public static final int NOTIFICATION_ID = 1;
Button btnBasicInlineReply;
TextView txtReplied;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btnBasicInlineReply = (Button) findViewById(R.id.btn_basic_inline_reply);
txtReplied = (TextView) findViewById(R.id.txt_inline_reply);
btnBasicInlineReply.setOnClickListener(this);
clearExistingNotifications()
}
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
processInlineReply(intent);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn_basic_inline_reply:
//Create notification builder
NotificationCompat.Builder builder = new NotificationCompat.Builder(this)
.setSmallIcon(android.R.drawable.stat_notify_chat)
.setContentTitle("Inline Reply Notification");
String replyLabel = "Enter your reply here";
//Initialise RemoteInput
RemoteInput remoteInput = new RemoteInput.Builder(KEY_REPLY)
.setLabel(replyLabel)
.build();
int randomRequestCode = new Random().nextInt(54325);
//PendingIntent that restarts the current activity instance.
Intent resultIntent = new Intent(this, MainActivity.class);
//Set a unique request code for this pending intent
PendingIntent resultPendingIntent = PendingIntent.getActivity(this, randomRequestCode, resultIntent, PendingIntent.FLAG_UPDATE_CURRENT);
//Notification Action with RemoteInput instance added.
NotificationCompat.Action replyAction = new NotificationCompat.Action.Builder(
android.R.drawable.sym_action_chat, "REPLY", resultPendingIntent)
.addRemoteInput(remoteInput)
.setAllowGeneratedReplies(true)
.build();
//Notification.Action instance added to Notification Builder.
builder.addAction(replyAction);
Intent intent = new Intent(this, MainActivity.class);
intent.putExtra("notificationId", NOTIFICATION_ID);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
PendingIntent dismissIntent = PendingIntent.getActivity(getBaseContext(), 0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
builder.addAction(android.R.drawable.ic_menu_close_clear_cancel, "DISMISS", dismissIntent);
//Create Notification.
NotificationManager notificationManager =
(NotificationManager)
getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(NOTIFICATION_ID,
builder.build());
break;
}
}
private void clearExistingNotifications() {
int notificationId = getIntent().getIntExtra("notificationId", 0);
NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
manager.cancel(notificationId);
}
private void processInlineReply(Intent intent) {
Bundle remoteInput = RemoteInput.getResultsFromIntent(intent);
if (remoteInput != null) {
String reply = remoteInput.getCharSequence(
KEY_REPLY).toString();
//Set the inline reply text in the TextView
txtReplied.setText("Reply is "+reply);
//Update the notification to show that the reply was received.
NotificationCompat.Builder repliedNotification =
new NotificationCompat.Builder(this)
.setSmallIcon(
android.R.drawable.stat_notify_chat)
.setContentText("Inline Reply received");
NotificationManager notificationManager =
(NotificationManager)
getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(NOTIFICATION_ID,
repliedNotification.build());
}
}
}
在上面的代码中,按下回复键时会调用onNewIntent(这要归功于Intent标志SINGLE_TOP和CLEAR_TOP)。
方法processInlineReply()是我们获取通知中输入的文本并在TextView中进行设置的地方。
然后更新通知(使用与创建时相同的NOTIFICATION_ID)以摆脱进度指示器并显示已收到回复。
在另一个世界中,我们可以通过使用以下代码片段取消通知,而不是更新通知。
//Cancel notification
NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
manager.cancel(NOTIFICATION_ID);
//Remove this from the MainActivity.java
NotificationManager notificationManager =
(NotificationManager)
getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(NOTIFICATION_ID,
repliedNotification.build());
上面更新的代码将在模拟器中提供以下输出。
注意:可以通过在构建器实例上添加多个addAction(remoteInput)实例来在通知中添加多个内联回复动作。
尽管建议每个通知仅使用一个内联回复操作按钮。
带有回复历史记录的通知
我们也可以借助setRemoteInputHistory方法在Notification中显示以前的内联响应。
在此应用程序中,我们将以LinkedList的形式存储以内联回复形式返回的字符序列,如下所示。
package com.theitroad.directreplynotification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.support.v4.app.NotificationCompat;
import android.support.v4.app.RemoteInput;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import java.util.LinkedList;
import java.util.List;
import java.util.Random;
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
String KEY_REPLY = "key_reply";
String KEY_REPLY_HISTORY = "key_reply_history";
public static final int NOTIFICATION_ID = 1;
Button btnBasicInlineReply, btnInlineReplyHistory;
TextView txtReplied;
private static List<CharSequence> responseHistory = new LinkedList<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btnBasicInlineReply = (Button) findViewById(R.id.btn_basic_inline_reply);
btnInlineReplyHistory = (Button) findViewById(R.id.btn_inline_replies_with_history);
txtReplied = (TextView) findViewById(R.id.txt_inline_reply);
btnBasicInlineReply.setOnClickListener(this);
btnInlineReplyHistory.setOnClickListener(this);
clearExistingNotifications();
}
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
processInlineReply(intent);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn_basic_inline_reply:
createInlineNotification();
break;
case R.id.btn_inline_replies_with_history:
if (!responseHistory.isEmpty()) {
CharSequence[] history = new CharSequence[responseHistory.size()];
createInlineNotificationWithHistory(responseHistory.toArray(history));
} else {
createInlineNotificationWithHistory(null);
}
break;
}
}
private void clearExistingNotifications() {
int notificationId = getIntent().getIntExtra("notificationId", 0);
NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
manager.cancel(notificationId);
}
private void processInlineReply(Intent intent) {
Bundle remoteInput = RemoteInput.getResultsFromIntent(intent);
if (remoteInput != null) {
CharSequence charSequence = remoteInput.getCharSequence(
KEY_REPLY);
if (charSequence != null) {
//Set the inline reply text in the TextView
String reply = charSequence.toString();
txtReplied.setText("Reply is " + reply);
//Update the notification to show that the reply was received.
NotificationCompat.Builder repliedNotification =
new NotificationCompat.Builder(this)
.setSmallIcon(
android.R.drawable.stat_notify_chat)
.setContentText("Inline Reply received");
NotificationManager notificationManager =
(NotificationManager)
getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(NOTIFICATION_ID,
repliedNotification.build());
/**Uncomment the below code to cancel the notification.
* Comment the above code too.
* **/
/*NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
manager.cancel(NOTIFICATION_ID);*/
} else {
String reply = remoteInput.getCharSequence(KEY_REPLY_HISTORY).toString();
responseHistory.add(0, reply);
if (!responseHistory.isEmpty()) {
CharSequence[] history = new CharSequence[responseHistory.size()];
createInlineNotificationWithHistory(responseHistory.toArray(history));
} else {
createInlineNotificationWithHistory(null);
}
}
}
}
private void createInlineNotification() {
//Create notification builder
NotificationCompat.Builder builder = new NotificationCompat.Builder(this)
.setSmallIcon(android.R.drawable.stat_notify_chat)
.setContentTitle("Inline Reply Notification");
String replyLabel = "Enter your reply here";
//Initialise RemoteInput
RemoteInput remoteInput = new RemoteInput.Builder(KEY_REPLY)
.setLabel(replyLabel)
.build();
int randomRequestCode = new Random().nextInt(54325);
//PendingIntent that restarts the current activity instance.
Intent resultIntent = new Intent(this, MainActivity.class);
resultIntent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_CLEAR_TOP);
//Set a unique request code for this pending intent
PendingIntent resultPendingIntent = PendingIntent.getActivity(this, randomRequestCode, resultIntent, PendingIntent.FLAG_UPDATE_CURRENT);
//Notification Action with RemoteInput instance added.
NotificationCompat.Action replyAction = new NotificationCompat.Action.Builder(
android.R.drawable.sym_action_chat, "REPLY", resultPendingIntent)
.addRemoteInput(remoteInput)
.setAllowGeneratedReplies(true)
.build();
//Notification.Action instance added to Notification Builder.
builder.addAction(replyAction);
Intent intent = new Intent(this, MainActivity.class);
intent.putExtra("notificationId", NOTIFICATION_ID);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
PendingIntent dismissIntent = PendingIntent.getActivity(getBaseContext(), 0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
builder.addAction(android.R.drawable.ic_menu_close_clear_cancel, "DISMISS", dismissIntent);
//Create Notification.
NotificationManager notificationManager =
(NotificationManager)
getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(NOTIFICATION_ID,
builder.build());
}
private void createInlineNotificationWithHistory(CharSequence[] history) {
//Create notification builder
NotificationCompat.Builder builder = new NotificationCompat.Builder(this)
.setSmallIcon(android.R.drawable.stat_notify_chat)
.setContentTitle("Inline Reply Notification With History");
String replyLabel = "Enter your reply here";
//Initialise RemoteInput
RemoteInput remoteInput = new RemoteInput.Builder(KEY_REPLY_HISTORY)
.setLabel(replyLabel)
.build();
int randomRequestCode = new Random().nextInt(54325);
//PendingIntent that restarts the current activity instance.
Intent resultIntent = new Intent(this, MainActivity.class);
resultIntent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_CLEAR_TOP);
//Set a unique request code for this pending intent
PendingIntent resultPendingIntent = PendingIntent.getActivity(this, randomRequestCode, resultIntent, PendingIntent.FLAG_UPDATE_CURRENT);
//Notification Action with RemoteInput instance added.
NotificationCompat.Action replyAction = new NotificationCompat.Action.Builder(
android.R.drawable.sym_action_chat, "REPLY", resultPendingIntent)
.addRemoteInput(remoteInput)
.setAllowGeneratedReplies(true)
.build();
//Notification.Action instance added to Notification Builder.
builder.addAction(replyAction);
Intent intent = new Intent(this, MainActivity.class);
intent.putExtra("notificationId", NOTIFICATION_ID);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
PendingIntent dismissIntent = PendingIntent.getActivity(getBaseContext(), 0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
builder.addAction(android.R.drawable.ic_menu_close_clear_cancel, "DISMISS", dismissIntent);
if (history != null) {
builder.setRemoteInputHistory(history);
}
//Create Notification.
NotificationManager notificationManager =
(NotificationManager)
getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(NOTIFICATION_ID,
builder.build());
}
}
我们添加了一个新按钮来处理带历史记录的内联回复。
在上面的代码中," responseHistory"保存了回复的历史记录。
以下代码段用于设置通知中的先前内联回复。
if (history != null) {
builder.setRemoteInputHistory(history);
}
方法processInlineReply()的ʻelse`部分使用新的内联回复历史记录更新当前通知,如下所示:
else {
String reply = remoteInput.getCharSequence(KEY_REPLY_HISTORY).toString();
responseHistory.add(0, reply);
if (!responseHistory.isEmpty()) {
CharSequence[] history = new CharSequence[responseHistory.size()];
createInlineNotificationWithHistory(responseHistory.toArray(history));
} else {
createInlineNotificationWithHistory(null);
}
}
responseHistory.add(0,reply);在回复历史记录中添加最新的内联回复。
注意:以上概念仅用于演示目的。
您可以根据需要进行调整。
内联回复不适用于Android N之前的版本。
因此,要使应用程序在其上起作用,可以在活动中添加基本EditText。

