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