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