Android N在通知欄上實(shí)現(xiàn)直接回復(fù)消息

Android N 版本中的通知又做了進(jìn)一步的改進(jìn)。主要改進(jìn)了如下幾點(diǎn):

  • 新的 UI 效果
  • 增強(qiáng)對自定義 View 的支持
  • 支持通知內(nèi)直接回復(fù)
  • 新的 MessagingStyle 樣式通知
  • 聚合通知 同一類型通知可以聚合一起了,再也不用擔(dān)心用戶手機(jī)滿屏都是通知了

剛好,我司的app是一款社交類型的app,為了適配Android N的這些特性,于是花了點(diǎn)時間給自己的app加上了通知欄直接回復(fù)的功能。直接上代碼:

public static void sendNotification(Context context, String tickerText, String title, String content, Intent intent, String user, int notifyId) {

    // 初始化NotificationManager
    NotificationManager messageNotificatioManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
    
    // 創(chuàng)建通知
    NotificationCompat.Builder builder = new NotificationCompat.Builder(context);
    builder.setTicker(content);
    builder.setContentInfo(tickerText);
    builder.setContentText(content);
    builder.setContentTitle(title);
    builder.setSmallIcon(R.drawable.icon_push);
    builder.setAutoCancel(true);

    // 設(shè)置通知的優(yōu)先級(懸浮通知)
    builder.setPriority(NotificationCompat.PRIORITY_MAX);
    
    // 根據(jù)用戶的偏好設(shè)置通知是否有聲音和震動
    boolean hasVoice = AppDataCache.getInstance().getIsVoice();
    boolean hasVibrate = AppDataCache.getInstance().getIsShake();
    if (hasVoice) {
        // 使用系統(tǒng)通知聲音
        Uri alarmSound = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
        // 設(shè)置通知的提示音
        builder.setSound(alarmSound);
    }
    if (hasVibrate) {
        long [] pattern = { 70, 150, 70, 150 };
        // 設(shè)置通知的震動
        builder.setVibrate(pattern);
    }

    builder.setWhen(System.currentTimeMillis());
    pendIntent = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
    builder.setContentIntent(pendIntent);
    
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
        adaptAndroidN(context, builder, user);
    }

    messageNotification = builder.build();
    messageNotification.flags = Notification.FLAG_AUTO_CANCEL;
    messageNotificatioManager.notify(notifyId, messageNotification);
}

另外像微信QQ,用戶在設(shè)置通知是否有聲音時,點(diǎn)擊選中時會播放一遍系統(tǒng)通知聲音,具體做法是這樣的:

// 播放系統(tǒng)通知鈴聲
public static void palySound(Context context) {
    Uri soundUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
    MediaPlayer mMediaPlayer = MediaPlayer.create(context, soundUri);
    mMediaPlayer.setLooping(false);
    mMediaPlayer.start();
}

其中RingtoneManager.TYPE_NOTIFICATION的值有如下幾種:

public static final int TYPE_RINGTONE = 1;
public static final int TYPE_NOTIFICATION = 2;
public static final int TYPE_ALARM = 4;
public static final int TYPE_ALL = TYPE_RINGTONE | TYPE_NOTIFICATION | TYPE_ALARM;

也就是通知聲音、鈴聲聲音、鬧鐘聲音和這三種的組合。

在Android N上通知要顯示時間,必須要設(shè)置下面這句才可以

builder.setShowWhen(true);

上面發(fā)送通知的方法中,直接在通知欄中回復(fù)的關(guān)鍵代碼就是

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
    adaptAndroidN(context, builder, user);
}
private static void adaptAndroidN(Context context, NotificationCompat.Builder builder, String user) {
    builder.setShowWhen(true);
    String replyLabel = "回復(fù)";
    RemoteInput remoteInput = new RemoteInput.Builder(KEY_TEXT_REPLY)
        .setLabel(replyLabel)
        .build();
    Intent intent = new Intent(context, ReplyService.class);
    intent.putExtra("userId", user);
    PendingIntent pendingIntent = PendingIntent.getService(context, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
    NotificationCompat.Action action = new NotificationCompat.Action.Builder(R.drawable.icon_push, replyLabel, pendingIntent)
            .addRemoteInput(remoteInput)
            .setAllowGeneratedReplies(true)
            .build();
    builder.addAction(action);
}

這里我只添加了一個回復(fù)按鈕,是可以添加多個的,看源碼發(fā)現(xiàn)addAction其實(shí)是把這個action添加到一個ArrayList中的,所有肯定是可以顯示多個按鈕的。

回復(fù)輸入內(nèi)容后,點(diǎn)擊右邊的發(fā)送按鈕,這個后續(xù)動作需要我們通過PendingIntent來實(shí)現(xiàn),比如我這里是要將這條消息發(fā)送給對方,所以我使用了IntentService來完成。

public class ReplyService extends IntentService implements SendMessageCallback {

    private static final String KEY_TEXT_REPLY = "key_text_reply";
    private DatabaseHelper helper;
    private SocketMessage tmpMsg;
    private String userId;

    public ReplyService() {
        super("ReplyService");
    }


    @Override protected void onHandleIntent(@Nullable Intent intent) {
        Bundle remoteInput = RemoteInput.getResultsFromIntent(intent);
        String message = null;
        if (remoteInput != null) {
            message = remoteInput.getCharSequence(KEY_TEXT_REPLY).toString();
        }

        if (StringUtil.isBlank(message)) {
            return;
        }

        if (intent.hasExtra("userId")) {
            userId = intent.getStringExtra("userId");
        }

        helper = new DatabaseHelper(this);

        SocketMessage socketMessage = new SocketMessage();
        socketMessage.setMsgType(1);// 文本
        socketMessage.setSender(0); // 自己發(fā)送的
        socketMessage.setUser(userId);// 對方用戶id
        socketMessage.setMsgContent(message);
        socketMessage.setMsgStamp(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
        socketMessage.setMsgStatus(0);
        socketMessage.setReadStatus(1);

        // 將本條發(fā)送的消息保存到數(shù)據(jù)庫
        helper.save(socketMessage);
        tmpMsg = socketMessage;

        // 發(fā)送消息
        SocketManager.sendMessage(this, socketMessage, this.getApplicationContext());
    }


    /**
     * 回調(diào)發(fā)送結(jié)果
     * @param code
     */
    @Override public void getCode(int code) {
        if (code == 1) {
            // 發(fā)送成功
            tmpMsg.setMsgStatus(1);
        } else {
            // 發(fā)送失敗
            tmpMsg.setMsgStatus(2);
        }
        // 更新數(shù)據(jù)庫中該記錄的發(fā)送狀態(tài)值
        helper.updateMsgStatus(tmpMsg);

        // 查詢與該用戶的未讀消息數(shù)
        int unReadCount = helper.findUnreadMessageCount(userId);

        // 設(shè)置該用戶所有的消息為已讀
        helper.setMessagesRead(userId);
        
        // 更新會話記錄表
        List<ChatPerson> chatPersonList = helper.findByCondition("userId", userId);
        ChatPerson cp = chatPersonList.get(0);
        cp.setMessage(tmpMsg.getMsgContent());
        cp.setLastseen(tmpMsg.getMsgStamp());
        helper.update(cp);
        
        // 發(fā)送廣播更新會話列表的未讀消息數(shù)
        sendBroadcast(new Intent(BroadcastAction.ACTION_REFRESH_CHAT_LIST));
        
        // 發(fā)送廣播更新底部tab上的未讀消息數(shù)(能還有其他用戶的未讀消息)
        int num = AppDataCache.getInstance().getUnReadMsgNum();
        AppDataCache.getInstance().setUnReadMsgNum(AppDataCache.getInstance().getUnReadMsgNum() - unReadCount);
        sendBroadcast(new Intent(BroadcastAction.ACTION_UPDATE_MESSAGE_NUM));
        
        // 操作完之后清除通知
        NotificationUtil.clearChatNotifications(this);
    }

}

關(guān)鍵點(diǎn)就是在onHandleIntent方法中通過RemoteInput獲取到剛輸入的信息:

Bundle remoteInput = RemoteInput.getResultsFromIntent(intent);
String message = null;
if (remoteInput != null) {
    message = remoteInput.getCharSequence(KEY_TEXT_REPLY).toString();
}

執(zhí)行完一系列操作后,IntentService自動結(jié)束,完成。

結(jié)尾處關(guān)于通知我有一點(diǎn)疑惑,就是在Android5.0之后的系統(tǒng),像QQ微信是剛下載安裝后它就有懸浮鎖屏顯示通知的權(quán)限,而普通的app是沒有的,即使我將優(yōu)先級設(shè)置為PRIORITY_MAX還是不行。不知道是不是國內(nèi)的手機(jī)廠商將QQ微信加入白名單了啊?如果讓用戶手動去設(shè)置這招怕是不行,因?yàn)椴⒉皇敲總€用戶都像Android開發(fā)者一樣愛去折騰手機(jī)。我目前是用Android7.1的原生系統(tǒng)測試的,是默認(rèn)可以顯示懸浮通知的。

參考文章
Android Nougat 的通知改進(jìn)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容