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