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。