Home | History | Annotate | Download | only in handlers
      1 /*
      2 Copyright 2016 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 package com.example.android.wearable.wear.wearnotifications.handlers;
     17 
     18 import android.app.IntentService;
     19 import android.app.Notification;
     20 import android.app.PendingIntent;
     21 import android.content.Intent;
     22 import android.graphics.BitmapFactory;
     23 import android.os.Build;
     24 import android.os.Bundle;
     25 import android.support.v4.app.NotificationCompat;
     26 import android.support.v4.app.NotificationCompat.MessagingStyle;
     27 import android.support.v4.app.NotificationManagerCompat;
     28 import android.support.v4.app.RemoteInput;
     29 import android.support.v4.app.TaskStackBuilder;
     30 import android.support.v4.content.ContextCompat;
     31 import android.util.Log;
     32 
     33 import com.example.android.wearable.wear.wearnotifications.GlobalNotificationBuilder;
     34 import com.example.android.wearable.wear.wearnotifications.MainActivity;
     35 import com.example.android.wearable.wear.wearnotifications.R;
     36 import com.example.android.wearable.wear.common.mock.MockDatabase;
     37 
     38 /**
     39  * Asynchronously handles updating messaging app posts (and active Notification) with replies from
     40  * user in a conversation. Notification for social app use MessagingStyle.
     41  */
     42 public class MessagingIntentService extends IntentService {
     43 
     44     private static final String TAG = "MessagingIntentService";
     45 
     46     public static final String ACTION_REPLY =
     47             "com.example.android.wearable.wear.wearnotifications.handlers.action.REPLY";
     48 
     49     public static final String EXTRA_REPLY =
     50             "com.example.android.wearable.wear.wearnotifications.handlers.extra.REPLY";
     51 
     52 
     53     public MessagingIntentService() {
     54         super("MessagingIntentService");
     55     }
     56 
     57     @Override
     58     protected void onHandleIntent(Intent intent) {
     59         Log.d(TAG, "onHandleIntent(): " + intent);
     60 
     61         if (intent != null) {
     62             final String action = intent.getAction();
     63             if (ACTION_REPLY.equals(action)) {
     64                 handleActionReply(getMessage(intent));
     65             }
     66         }
     67     }
     68 
     69     /**
     70      * Handles action for replying to messages from the notification.
     71      */
     72     private void handleActionReply(CharSequence replyCharSequence) {
     73         Log.d(TAG, "handleActionReply(): " + replyCharSequence);
     74 
     75         if (replyCharSequence != null) {
     76 
     77             // TODO: Asynchronously save your message to Database and servers.
     78 
     79             /*
     80              * You have two options for updating your notification (this class uses approach #2):
     81              *
     82              *  1. Use a new NotificationCompatBuilder to create the Notification. This approach
     83              *  requires you to get *ALL* the information that existed in the previous
     84              *  Notification (and updates) and pass it to the builder. This is the approach used in
     85              *  the MainActivity.
     86              *
     87              *  2. Use the original NotificationCompatBuilder to create the Notification. This
     88              *  approach requires you to store a reference to the original builder. The benefit is
     89              *  you only need the new/updated information. In our case, the reply from the user
     90              *  which we already have here.
     91              *
     92              *  IMPORTANT NOTE: You shouldn't save/modify the resulting Notification object using
     93              *  its member variables and/or legacy APIs. If you want to retain anything from update
     94              *  to update, retain the Builder as option 2 outlines.
     95              */
     96 
     97             // Retrieves NotificationCompat.Builder used to create initial Notification
     98             NotificationCompat.Builder notificationCompatBuilder =
     99                     GlobalNotificationBuilder.getNotificationCompatBuilderInstance();
    100 
    101             // Recreate builder from persistent state if app process is killed
    102             if (notificationCompatBuilder == null) {
    103                 // Note: New builder set globally in the method
    104                 notificationCompatBuilder = recreateBuilderWithMessagingStyle();
    105             }
    106 
    107 
    108             // Since we are adding to the MessagingStyle, we need to first retrieve the
    109             // current MessagingStyle from the Notification itself.
    110             Notification notification = notificationCompatBuilder.build();
    111             MessagingStyle messagingStyle =
    112                     NotificationCompat.MessagingStyle
    113                             .extractMessagingStyleFromNotification(notification);
    114 
    115             // Add new message to the MessagingStyle
    116             messagingStyle.addMessage(replyCharSequence, System.currentTimeMillis(), null);
    117 
    118             // Updates the Notification
    119             notification = notificationCompatBuilder
    120                     .setStyle(messagingStyle)
    121                     .build();
    122 
    123             // Pushes out the updated Notification
    124             NotificationManagerCompat notificationManagerCompat =
    125                     NotificationManagerCompat.from(getApplicationContext());
    126             notificationManagerCompat.notify(MainActivity.NOTIFICATION_ID, notification);
    127         }
    128     }
    129 
    130     /*
    131      * Extracts CharSequence created from the RemoteInput associated with the Notification.
    132      */
    133     private CharSequence getMessage(Intent intent) {
    134         Bundle remoteInput = RemoteInput.getResultsFromIntent(intent);
    135         if (remoteInput != null) {
    136             return remoteInput.getCharSequence(EXTRA_REPLY);
    137         }
    138         return null;
    139     }
    140 
    141     /*
    142      * This recreates the notification from the persistent state in case the app process was killed.
    143      * It is basically the same code for creating the Notification from MainActivity.
    144      */
    145     private NotificationCompat.Builder recreateBuilderWithMessagingStyle() {
    146 
    147         // Main steps for building a MESSAGING_STYLE notification (for more detailed comments on
    148         // building this notification, check MainActivity.java):
    149         //      0. Get your data
    150         //      1. Retrieve Notification Channel for O and beyond devices (26+)
    151         //      2. Build the MESSAGING_STYLE
    152         //      3. Set up main Intent for notification
    153         //      4. Set up RemoteInput (users can input directly from notification)
    154         //      5. Build and issue the notification
    155 
    156         // 0. Get your data.
    157         MockDatabase.MessagingStyleCommsAppData messagingData =
    158                 MockDatabase.getMessagingStyleData();
    159 
    160         // 1. Retrieve Notification Channel for O and beyond devices (26+). We don't need to create
    161         //    the NotificationChannel, since it was created the first time this Notification was
    162         //    created.
    163         String notificationChannelId = messagingData.getChannelId();
    164 
    165         // 2. Build the Notification.Style (MESSAGING_STYLE).
    166         String contentTitle = messagingData.getContentTitle();
    167 
    168         MessagingStyle messagingStyle =
    169                 new NotificationCompat.MessagingStyle(messagingData.getReplayName())
    170                         .setConversationTitle(contentTitle);
    171 
    172         // Adds all Messages.
    173         // Note: Messages include the text, timestamp, and sender.
    174         for (MessagingStyle.Message message : messagingData.getMessages()) {
    175             messagingStyle.addMessage(message);
    176         }
    177 
    178 
    179         // 3. Set up main Intent for notification.
    180         Intent notifyIntent = new Intent(this, MessagingMainActivity.class);
    181 
    182         TaskStackBuilder stackBuilder = TaskStackBuilder.create(this);
    183         stackBuilder.addParentStack(MessagingMainActivity.class);
    184         stackBuilder.addNextIntent(notifyIntent);
    185 
    186         PendingIntent mainPendingIntent =
    187                 PendingIntent.getActivity(
    188                         this,
    189                         0,
    190                         notifyIntent,
    191                         PendingIntent.FLAG_UPDATE_CURRENT
    192                 );
    193 
    194 
    195         // 4. Set up RemoteInput, so users can input (keyboard and voice) from notification.
    196         String replyLabel = getString(R.string.reply_label);
    197         RemoteInput remoteInput = new RemoteInput.Builder(MessagingIntentService.EXTRA_REPLY)
    198                 .setLabel(replyLabel)
    199                 .build();
    200 
    201         PendingIntent replyActionPendingIntent;
    202 
    203         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
    204             Intent intent = new Intent(this, MessagingIntentService.class);
    205             intent.setAction(MessagingIntentService.ACTION_REPLY);
    206             replyActionPendingIntent = PendingIntent.getService(this, 0, intent, 0);
    207 
    208         } else {
    209             replyActionPendingIntent = mainPendingIntent;
    210         }
    211 
    212         NotificationCompat.Action replyAction =
    213                 new NotificationCompat.Action.Builder(
    214                         R.drawable.ic_reply_white_18dp,
    215                         replyLabel,
    216                         replyActionPendingIntent)
    217                         .addRemoteInput(remoteInput)
    218                         // Allows system to generate replies by context of conversation.
    219                         .setAllowGeneratedReplies(true)
    220                         .build();
    221 
    222 
    223         // 5. Build and issue the notification.
    224 
    225         // Notification Channel Id is ignored for Android pre O (26).
    226         NotificationCompat.Builder notificationCompatBuilder =
    227                 new NotificationCompat.Builder(
    228                         getApplicationContext(), notificationChannelId);
    229 
    230         GlobalNotificationBuilder.setNotificationCompatBuilderInstance(notificationCompatBuilder);
    231 
    232         notificationCompatBuilder
    233                 .setStyle(messagingStyle)
    234                 .setContentTitle(contentTitle)
    235                 .setContentText(messagingData.getContentText())
    236                 .setSmallIcon(R.drawable.ic_launcher)
    237                 .setLargeIcon(BitmapFactory.decodeResource(
    238                         getResources(),
    239                         R.drawable.ic_person_black_48dp))
    240                 .setContentIntent(mainPendingIntent)
    241                 .setColor(ContextCompat.getColor(getApplicationContext(), R.color.colorPrimary))
    242                 .setSubText(Integer.toString(messagingData.getNumberOfNewMessages()))
    243                 .addAction(replyAction)
    244                 .setCategory(Notification.CATEGORY_MESSAGE)
    245                 .setPriority(messagingData.getPriority())
    246                 .setVisibility(messagingData.getChannelLockscreenVisibility());
    247 
    248         for (String name : messagingData.getParticipants()) {
    249             notificationCompatBuilder.addPerson(name);
    250         }
    251 
    252         return notificationCompatBuilder;
    253     }
    254 }