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