1 /* 2 * Copyright (C) 2014 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.example.android.messagingservice; 18 19 import android.app.PendingIntent; 20 import android.app.Service; 21 import android.content.Intent; 22 import android.graphics.BitmapFactory; 23 import android.os.Handler; 24 import android.os.IBinder; 25 import android.os.Message; 26 import android.os.Messenger; 27 import android.support.v4.app.NotificationCompat.CarExtender; 28 import android.support.v4.app.NotificationCompat.CarExtender.UnreadConversation; 29 import android.support.v4.app.NotificationCompat; 30 import android.support.v4.app.NotificationManagerCompat; 31 import android.support.v4.app.RemoteInput; 32 import android.util.Log; 33 34 import java.lang.ref.WeakReference; 35 import java.util.Iterator; 36 37 public class MessagingService extends Service { 38 private static final String TAG = MessagingService.class.getSimpleName(); 39 private static final String EOL = "\n"; 40 private static final String READ_ACTION = 41 "com.example.android.messagingservice.ACTION_MESSAGE_READ"; 42 public static final String REPLY_ACTION = 43 "com.example.android.messagingservice.ACTION_MESSAGE_REPLY"; 44 public static final String CONVERSATION_ID = "conversation_id"; 45 public static final String EXTRA_REMOTE_REPLY = "extra_remote_reply"; 46 public static final int MSG_SEND_NOTIFICATION = 1; 47 48 private NotificationManagerCompat mNotificationManager; 49 50 private final Messenger mMessenger = new Messenger(new IncomingHandler(this)); 51 52 @Override onCreate()53 public void onCreate() { 54 Log.d(TAG, "onCreate"); 55 mNotificationManager = NotificationManagerCompat.from(getApplicationContext()); 56 } 57 58 @Override onBind(Intent intent)59 public IBinder onBind(Intent intent) { 60 Log.d(TAG, "onBind"); 61 return mMessenger.getBinder(); 62 } 63 64 // Creates an intent that will be triggered when a message is marked as read. getMessageReadIntent(int id)65 private Intent getMessageReadIntent(int id) { 66 return new Intent() 67 .addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES) 68 .setAction(READ_ACTION) 69 .putExtra(CONVERSATION_ID, id); 70 } 71 72 // Creates an Intent that will be triggered when a voice reply is received. getMessageReplyIntent(int conversationId)73 private Intent getMessageReplyIntent(int conversationId) { 74 return new Intent() 75 .addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES) 76 .setAction(REPLY_ACTION) 77 .putExtra(CONVERSATION_ID, conversationId); 78 } 79 sendNotification(int howManyConversations, int messagesPerConversation)80 private void sendNotification(int howManyConversations, int messagesPerConversation) { 81 Conversations.Conversation[] conversations = Conversations.getUnreadConversations( 82 howManyConversations, messagesPerConversation); 83 for (Conversations.Conversation conv : conversations) { 84 sendNotificationForConversation(conv); 85 } 86 } 87 sendNotificationForConversation(Conversations.Conversation conversation)88 private void sendNotificationForConversation(Conversations.Conversation conversation) { 89 // A pending Intent for reads 90 PendingIntent readPendingIntent = PendingIntent.getBroadcast(getApplicationContext(), 91 conversation.getConversationId(), 92 getMessageReadIntent(conversation.getConversationId()), 93 PendingIntent.FLAG_UPDATE_CURRENT); 94 95 // Build a RemoteInput for receiving voice input in a Car Notification or text input on 96 // devices that support text input (like devices on Android N and above). 97 RemoteInput remoteInput = new RemoteInput.Builder(EXTRA_REMOTE_REPLY) 98 .setLabel(getString(R.string.reply)) 99 .build(); 100 101 // Building a Pending Intent for the reply action to trigger 102 PendingIntent replyIntent = PendingIntent.getBroadcast(getApplicationContext(), 103 conversation.getConversationId(), 104 getMessageReplyIntent(conversation.getConversationId()), 105 PendingIntent.FLAG_UPDATE_CURRENT); 106 107 // Build an Android N compatible Remote Input enabled action. 108 NotificationCompat.Action actionReplyByRemoteInput = new NotificationCompat.Action.Builder( 109 R.drawable.notification_icon, getString(R.string.reply), replyIntent) 110 .addRemoteInput(remoteInput) 111 .build(); 112 113 // Create the UnreadConversation and populate it with the participant name, 114 // read and reply intents. 115 UnreadConversation.Builder unreadConvBuilder = 116 new UnreadConversation.Builder(conversation.getParticipantName()) 117 .setLatestTimestamp(conversation.getTimestamp()) 118 .setReadPendingIntent(readPendingIntent) 119 .setReplyAction(replyIntent, remoteInput); 120 121 // Note: Add messages from oldest to newest to the UnreadConversation.Builder 122 StringBuilder messageForNotification = new StringBuilder(); 123 for (Iterator<String> messages = conversation.getMessages().iterator(); 124 messages.hasNext(); ) { 125 String message = messages.next(); 126 unreadConvBuilder.addMessage(message); 127 messageForNotification.append(message); 128 if (messages.hasNext()) { 129 messageForNotification.append(EOL); 130 } 131 } 132 133 NotificationCompat.Builder builder = new NotificationCompat.Builder(getApplicationContext()) 134 .setSmallIcon(R.drawable.notification_icon) 135 .setLargeIcon(BitmapFactory.decodeResource( 136 getApplicationContext().getResources(), R.drawable.android_contact)) 137 .setContentText(messageForNotification.toString()) 138 .setWhen(conversation.getTimestamp()) 139 .setContentTitle(conversation.getParticipantName()) 140 .setContentIntent(readPendingIntent) 141 .extend(new CarExtender() 142 .setUnreadConversation(unreadConvBuilder.build()) 143 .setColor(getApplicationContext().getResources() 144 .getColor(R.color.default_color_light))) 145 .addAction(actionReplyByRemoteInput); 146 147 MessageLogger.logMessage(getApplicationContext(), "Sending notification " 148 + conversation.getConversationId() + " conversation: " + conversation); 149 150 mNotificationManager.notify(conversation.getConversationId(), builder.build()); 151 } 152 153 /** 154 * Handler for incoming messages from clients. 155 */ 156 private static class IncomingHandler extends Handler { 157 private final WeakReference<MessagingService> mReference; 158 IncomingHandler(MessagingService service)159 IncomingHandler(MessagingService service) { 160 mReference = new WeakReference<>(service); 161 } 162 163 @Override handleMessage(Message msg)164 public void handleMessage(Message msg) { 165 MessagingService service = mReference.get(); 166 switch (msg.what) { 167 case MSG_SEND_NOTIFICATION: 168 int howManyConversations = msg.arg1 <= 0 ? 1 : msg.arg1; 169 int messagesPerConversation = msg.arg2 <= 0 ? 1 : msg.arg2; 170 if (service != null) { 171 service.sendNotification(howManyConversations, messagesPerConversation); 172 } 173 break; 174 default: 175 super.handleMessage(msg); 176 } 177 } 178 } 179 } 180