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.BigPictureStyle;
26 import android.support.v4.app.NotificationManagerCompat;
27 import android.support.v4.app.RemoteInput;
28 import android.support.v4.app.TaskStackBuilder;
29 import android.support.v7.app.NotificationCompat;
30 import android.util.Log;
31 
32 import com.example.android.wearable.wear.wearnotifications.GlobalNotificationBuilder;
33 import com.example.android.wearable.wear.wearnotifications.MainActivity;
34 import com.example.android.wearable.wear.wearnotifications.R;
35 import com.example.android.wearable.wear.wearnotifications.mock.MockDatabase;
36 
37 /**
38  * Asynchronously handles updating social app posts (and active Notification) with comments from
39  * user. Notification for social app use BigPictureStyle.
40  */
41 public class BigPictureSocialIntentService extends IntentService {
42 
43     private static final String TAG = "BigPictureService";
44 
45     public static final String ACTION_COMMENT =
46             "com.example.android.wearable.wear.wearnotifications.handlers.action.COMMENT";
47 
48     public static final String EXTRA_COMMENT =
49             "com.example.android.wearable.wear.wearnotifications.handlers.extra.COMMENT";
50 
BigPictureSocialIntentService()51     public BigPictureSocialIntentService() {
52         super("BigPictureSocialIntentService");
53     }
54 
55     @Override
onHandleIntent(Intent intent)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_COMMENT.equals(action)) {
62                 handleActionComment(getMessage(intent));
63             }
64         }
65     }
66 
67     /**
68      * Handles action for adding a comment from the notification.
69      */
handleActionComment(CharSequence comment)70     private void handleActionComment(CharSequence comment) {
71         Log.d(TAG, "handleActionComment(): " + comment);
72 
73         if (comment != 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 comment from the user
88              *  regarding the post (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 = recreateBuilderWithBigPictureStyle();
103             }
104 
105             // Updates active Notification
106             Notification updatedNotification = notificationCompatBuilder
107                     // Adds a line and comment below content in Notification
108                     .setRemoteInputHistory(new CharSequence[]{comment})
109                     .build();
110 
111             // Pushes out the updated Notification
112             NotificationManagerCompat notificationManagerCompat =
113                     NotificationManagerCompat.from(getApplicationContext());
114             notificationManagerCompat.notify(MainActivity.NOTIFICATION_ID, updatedNotification);
115         }
116     }
117 
118     /*
119      * Extracts CharSequence created from the RemoteInput associated with the Notification.
120      */
getMessage(Intent intent)121     private CharSequence getMessage(Intent intent) {
122         Bundle remoteInput = RemoteInput.getResultsFromIntent(intent);
123         if (remoteInput != null) {
124             return remoteInput.getCharSequence(EXTRA_COMMENT);
125         }
126         return null;
127     }
128 
129     /*
130      * This recreates the notification from the persistent state in case the app process was killed.
131      * It is basically the same code for creating the Notification from MainActivity.
132      */
recreateBuilderWithBigPictureStyle()133     private NotificationCompat.Builder recreateBuilderWithBigPictureStyle() {
134 
135         // Main steps for building a BIG_PICTURE_STYLE notification (for more detailed comments on
136         // building this notification, check MainActivity.java):
137         //      0. Get your data
138         //      1. Build the BIG_PICTURE_STYLE
139         //      2. Set up main Intent for notification
140         //      3. Set up RemoteInput, so users can input (keyboard and voice) from notification
141         //      4. Build and issue the notification
142 
143         // 0. Get your data (everything unique per Notification)
144         MockDatabase.BigPictureStyleSocialAppData bigPictureStyleSocialAppData =
145                 MockDatabase.getBigPictureStyleData();
146 
147         // 1. Build the BIG_PICTURE_STYLE
148         BigPictureStyle bigPictureStyle = new NotificationCompat.BigPictureStyle()
149                 .bigPicture(
150                         BitmapFactory.decodeResource(
151                                 getResources(),
152                                 bigPictureStyleSocialAppData.getBigImage()))
153                 .setBigContentTitle(bigPictureStyleSocialAppData.getBigContentTitle())
154                 .setSummaryText(bigPictureStyleSocialAppData.getSummaryText());
155 
156         // 2. Set up main Intent for notification
157         Intent mainIntent = new Intent(this, BigPictureSocialMainActivity.class);
158 
159         TaskStackBuilder stackBuilder = TaskStackBuilder.create(this);
160         stackBuilder.addParentStack(BigPictureSocialMainActivity.class);
161         stackBuilder.addNextIntent(mainIntent);
162 
163         PendingIntent mainPendingIntent =
164                 PendingIntent.getActivity(
165                         this,
166                         0,
167                         mainIntent,
168                         PendingIntent.FLAG_UPDATE_CURRENT
169                 );
170 
171         // 3. Set up RemoteInput, so users can input (keyboard and voice) from notification
172         String replyLabel = getString(R.string.reply_label);
173         RemoteInput remoteInput =
174                 new RemoteInput.Builder(BigPictureSocialIntentService.EXTRA_COMMENT)
175                         .setLabel(replyLabel)
176                         .setChoices(bigPictureStyleSocialAppData.getPossiblePostResponses())
177                         .build();
178 
179         PendingIntent replyActionPendingIntent;
180 
181         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
182             Intent intent = new Intent(this, BigPictureSocialIntentService.class);
183             intent.setAction(BigPictureSocialIntentService.ACTION_COMMENT);
184             replyActionPendingIntent = PendingIntent.getService(this, 0, intent, 0);
185 
186         } else {
187             replyActionPendingIntent = mainPendingIntent;
188         }
189 
190         NotificationCompat.Action replyAction =
191                 new NotificationCompat.Action.Builder(
192                         R.drawable.ic_reply_white_18dp,
193                         replyLabel,
194                         replyActionPendingIntent)
195                         .addRemoteInput(remoteInput)
196                         .build();
197 
198         // 4. Build and issue the notification
199         NotificationCompat.Builder notificationCompatBuilder =
200                 new NotificationCompat.Builder(getApplicationContext());
201 
202         GlobalNotificationBuilder.setNotificationCompatBuilderInstance(notificationCompatBuilder);
203 
204         notificationCompatBuilder
205                 .setStyle(bigPictureStyle)
206                 .setContentTitle(bigPictureStyleSocialAppData.getContentTitle())
207                 .setContentText(bigPictureStyleSocialAppData.getContentText())
208                 .setSmallIcon(R.drawable.ic_launcher)
209                 .setLargeIcon(BitmapFactory.decodeResource(
210                         getResources(),
211                         R.drawable.ic_person_black_48dp))
212                 .setContentIntent(mainPendingIntent)
213                 .setColor(getResources().getColor(R.color.colorPrimary))
214                 .setSubText(Integer.toString(1))
215                 .addAction(replyAction)
216                 .setCategory(Notification.CATEGORY_SOCIAL)
217                 .setPriority(Notification.PRIORITY_HIGH)
218                 .setVisibility(Notification.VISIBILITY_PRIVATE);
219 
220         // If the phone is in "Do not disturb mode, the user will still be notified if
221         // the sender(s) is starred as a favorite.
222         for (String name : bigPictureStyleSocialAppData.getParticipants()) {
223             notificationCompatBuilder.addPerson(name);
224         }
225 
226         return notificationCompatBuilder;
227     }
228 }