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 43 public static final String REPLY_ACTION = 44 "com.example.android.messagingservice.ACTION_MESSAGE_REPLY"; 45 public static final String CONVERSATION_ID = "conversation_id"; 46 public static final String EXTRA_VOICE_REPLY = "extra_voice_reply"; 47 public static final int MSG_SEND_NOTIFICATION = 1; 48 49 private NotificationManagerCompat mNotificationManager; 50 51 private final Messenger mMessenger = new Messenger(new IncomingHandler(this)); 52 53 @Override onCreate()54 public void onCreate() { 55 Log.d(TAG, "onCreate"); 56 mNotificationManager = NotificationManagerCompat.from(getApplicationContext()); 57 } 58 59 @Override onBind(Intent intent)60 public IBinder onBind(Intent intent) { 61 Log.d(TAG, "onBind"); 62 return mMessenger.getBinder(); 63 } 64 65 // Creates an intent that will be triggered when a message is marked as read. getMessageReadIntent(int id)66 private Intent getMessageReadIntent(int id) { 67 return new Intent() 68 .addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES) 69 .setAction(READ_ACTION) 70 .putExtra(CONVERSATION_ID, id); 71 } 72 73 // Creates an Intent that will be triggered when a voice reply is received. getMessageReplyIntent(int conversationId)74 private Intent getMessageReplyIntent(int conversationId) { 75 return new Intent() 76 .addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES) 77 .setAction(REPLY_ACTION) 78 .putExtra(CONVERSATION_ID, conversationId); 79 } 80 sendNotification(int howManyConversations, int messagesPerConversation)81 private void sendNotification(int howManyConversations, int messagesPerConversation) { 82 Conversations.Conversation[] conversations = Conversations.getUnreadConversations( 83 howManyConversations, messagesPerConversation); 84 for (Conversations.Conversation conv : conversations) { 85 sendNotificationForConversation(conv); 86 } 87 } 88 sendNotificationForConversation(Conversations.Conversation conversation)89 private void sendNotificationForConversation(Conversations.Conversation conversation) { 90 // A pending Intent for reads 91 PendingIntent readPendingIntent = PendingIntent.getBroadcast(getApplicationContext(), 92 conversation.getConversationId(), 93 getMessageReadIntent(conversation.getConversationId()), 94 PendingIntent.FLAG_UPDATE_CURRENT); 95 96 // Build a RemoteInput for receiving voice input in a Car Notification 97 RemoteInput remoteInput = new RemoteInput.Builder(EXTRA_VOICE_REPLY) 98 .setLabel(getApplicationContext().getString(R.string.notification_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 // Create the UnreadConversation and populate it with the participant name, 108 // read and reply intents. 109 UnreadConversation.Builder unreadConvBuilder = 110 new UnreadConversation.Builder(conversation.getParticipantName()) 111 .setLatestTimestamp(conversation.getTimestamp()) 112 .setReadPendingIntent(readPendingIntent) 113 .setReplyAction(replyIntent, remoteInput); 114 115 // Note: Add messages from oldest to newest to the UnreadConversation.Builder 116 StringBuilder messageForNotification = new StringBuilder(); 117 for (Iterator<String> messages = conversation.getMessages().iterator(); 118 messages.hasNext(); ) { 119 String message = messages.next(); 120 unreadConvBuilder.addMessage(message); 121 messageForNotification.append(message); 122 if (messages.hasNext()) { 123 messageForNotification.append(EOL); 124 } 125 } 126 127 NotificationCompat.Builder builder = new NotificationCompat.Builder(getApplicationContext()) 128 .setSmallIcon(R.drawable.notification_icon) 129 .setLargeIcon(BitmapFactory.decodeResource( 130 getApplicationContext().getResources(), R.drawable.android_contact)) 131 .setContentText(messageForNotification.toString()) 132 .setWhen(conversation.getTimestamp()) 133 .setContentTitle(conversation.getParticipantName()) 134 .setContentIntent(readPendingIntent) 135 .extend(new CarExtender() 136 .setUnreadConversation(unreadConvBuilder.build()) 137 .setColor(getApplicationContext() 138 .getResources().getColor(R.color.default_color_light))); 139 140 MessageLogger.logMessage(getApplicationContext(), "Sending notification " 141 + conversation.getConversationId() + " conversation: " + conversation); 142 143 mNotificationManager.notify(conversation.getConversationId(), builder.build()); 144 } 145 146 /** 147 * Handler for incoming messages from clients. 148 */ 149 private static class IncomingHandler extends Handler { 150 private final WeakReference<MessagingService> mReference; 151 IncomingHandler(MessagingService service)152 IncomingHandler(MessagingService service) { 153 mReference = new WeakReference<>(service); 154 } 155 156 @Override handleMessage(Message msg)157 public void handleMessage(Message msg) { 158 MessagingService service = mReference.get(); 159 switch (msg.what) { 160 case MSG_SEND_NOTIFICATION: 161 int howManyConversations = msg.arg1 <= 0 ? 1 : msg.arg1; 162 int messagesPerConversation = msg.arg2 <= 0 ? 1 : msg.arg2; 163 if (service != null) { 164 service.sendNotification(howManyConversations, messagesPerConversation); 165 } 166 break; 167 default: 168 super.handleMessage(msg); 169 } 170 } 171 } 172 } 173