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.support.v4.app.NotificationCompat; 25 import android.support.v4.app.NotificationCompat.BigTextStyle; 26 import android.support.v4.app.NotificationManagerCompat; 27 import android.support.v4.content.ContextCompat; 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.common.mock.MockDatabase; 34 35 import java.util.concurrent.TimeUnit; 36 37 /** 38 * Asynchronously handles snooze and dismiss actions for reminder app (and active Notification). 39 * Notification for for reminder app uses BigTextStyle. 40 */ 41 public class BigTextIntentService extends IntentService { 42 43 private static final String TAG = "BigTextService"; 44 45 public static final String ACTION_DISMISS = 46 "com.example.android.wearable.wear.wearnotifications.handlers.action.DISMISS"; 47 public static final String ACTION_SNOOZE = 48 "com.example.android.wearable.wear.wearnotifications.handlers.action.SNOOZE"; 49 50 private static final long SNOOZE_TIME = TimeUnit.SECONDS.toMillis(5); 51 52 public BigTextIntentService() { 53 super("BigTextIntentService"); 54 } 55 56 @Override 57 protected void onHandleIntent(Intent intent) { 58 Log.d(TAG, "onHandleIntent(): " + intent); 59 60 if (intent != null) { 61 final String action = intent.getAction(); 62 if (ACTION_DISMISS.equals(action)) { 63 handleActionDismiss(); 64 } else if (ACTION_SNOOZE.equals(action)) { 65 handleActionSnooze(); 66 } 67 } 68 } 69 70 /** 71 * Handles action Dismiss in the provided background thread. 72 */ 73 private void handleActionDismiss() { 74 Log.d(TAG, "handleActionDismiss()"); 75 76 NotificationManagerCompat notificationManagerCompat = 77 NotificationManagerCompat.from(getApplicationContext()); 78 notificationManagerCompat.cancel(StandaloneMainActivity.NOTIFICATION_ID); 79 } 80 81 /** 82 * Handles action Snooze in the provided background thread. 83 */ 84 private void handleActionSnooze() { 85 Log.d(TAG, "handleActionSnooze()"); 86 87 // You could use NotificationManager.getActiveNotifications() if you are targeting SDK 23 88 // and above, but we are targeting devices with lower SDK API numbers, so we saved the 89 // builder globally and get the notification back to recreate it later. 90 91 NotificationCompat.Builder notificationCompatBuilder = 92 GlobalNotificationBuilder.getNotificationCompatBuilderInstance(); 93 94 // Recreate builder from persistent state if app process is killed 95 if (notificationCompatBuilder == null) { 96 // Note: New builder set globally in the method 97 notificationCompatBuilder = recreateBuilderWithBigTextStyle(); 98 } 99 100 Notification notification; 101 notification = notificationCompatBuilder.build(); 102 103 104 if (notification != null) { 105 NotificationManagerCompat notificationManagerCompat = 106 NotificationManagerCompat.from(getApplicationContext()); 107 108 notificationManagerCompat.cancel(StandaloneMainActivity.NOTIFICATION_ID); 109 110 try { 111 Thread.sleep(SNOOZE_TIME); 112 } catch (InterruptedException ex) { 113 Thread.currentThread().interrupt(); 114 } 115 notificationManagerCompat.notify(StandaloneMainActivity.NOTIFICATION_ID, notification); 116 } 117 118 } 119 120 /* 121 * This recreates the notification from the persistent state in case the app process was killed. 122 * It is basically the same code for creating the Notification from StandaloneMainActivity. 123 */ 124 private NotificationCompat.Builder recreateBuilderWithBigTextStyle() { 125 126 // Main steps for building a BIG_TEXT_STYLE notification (for more detailed comments on 127 // building this notification, check StandaloneMainActivity.java): 128 // 0. Get your data 129 // 1. Retrieve Notification Channel for O and beyond devices (26+) 130 // 2. Build the BIG_TEXT_STYLE 131 // 3. Set up main Intent for notification 132 // 4. Create additional Actions for the Notification 133 // 5. Build and issue the notification 134 135 // 0. Get your data (everything unique per Notification). 136 MockDatabase.BigTextStyleReminderAppData bigTextStyleReminderAppData = 137 MockDatabase.getBigTextStyleData(); 138 139 // 1. Retrieve Notification Channel for O and beyond devices (26+). We don't need to create 140 // the NotificationChannel, since it was created the first time this Notification was 141 // created. 142 String notificationChannelId = bigTextStyleReminderAppData.getChannelId(); 143 144 // 2. Build the BIG_TEXT_STYLE. 145 BigTextStyle bigTextStyle = new NotificationCompat.BigTextStyle() 146 .bigText(bigTextStyleReminderAppData.getBigText()) 147 .setBigContentTitle(bigTextStyleReminderAppData.getBigContentTitle()) 148 .setSummaryText(bigTextStyleReminderAppData.getSummaryText()); 149 150 151 // 3. Set up main Intent for notification. 152 Intent mainIntent = new Intent(this, BigTextMainActivity.class); 153 154 PendingIntent mainPendingIntent = 155 PendingIntent.getActivity( 156 this, 157 0, 158 mainIntent, 159 PendingIntent.FLAG_UPDATE_CURRENT 160 ); 161 162 163 // 4. Create additional Actions (Intents) for the Notification. 164 165 // Snooze Action. 166 Intent snoozeIntent = new Intent(this, BigTextIntentService.class); 167 snoozeIntent.setAction(BigTextIntentService.ACTION_SNOOZE); 168 169 PendingIntent snoozePendingIntent = PendingIntent.getService(this, 0, snoozeIntent, 0); 170 NotificationCompat.Action snoozeAction = 171 new NotificationCompat.Action.Builder( 172 R.drawable.ic_alarm_white_48dp, 173 "Snooze", 174 snoozePendingIntent) 175 .build(); 176 177 // Dismiss Action 178 Intent dismissIntent = new Intent(this, BigTextIntentService.class); 179 dismissIntent.setAction(BigTextIntentService.ACTION_DISMISS); 180 181 PendingIntent dismissPendingIntent = PendingIntent.getService(this, 0, dismissIntent, 0); 182 NotificationCompat.Action dismissAction = 183 new NotificationCompat.Action.Builder( 184 R.drawable.ic_cancel_white_48dp, 185 "Dismiss", 186 dismissPendingIntent) 187 .build(); 188 189 190 // 5. Build and issue the notification. 191 192 // Notification Channel Id is ignored for Android pre O (26). 193 NotificationCompat.Builder notificationCompatBuilder = 194 new NotificationCompat.Builder( 195 getApplicationContext(), notificationChannelId); 196 197 GlobalNotificationBuilder.setNotificationCompatBuilderInstance(notificationCompatBuilder); 198 199 notificationCompatBuilder 200 .setStyle(bigTextStyle) 201 .setContentTitle(bigTextStyleReminderAppData.getContentTitle()) 202 .setContentText(bigTextStyleReminderAppData.getContentText()) 203 .setSmallIcon(R.drawable.ic_launcher) 204 .setLargeIcon(BitmapFactory.decodeResource( 205 getResources(), 206 R.drawable.ic_alarm_white_48dp)) 207 .setColor(ContextCompat.getColor(getApplicationContext(), R.color.colorPrimary)) 208 .setCategory(Notification.CATEGORY_REMINDER) 209 .setPriority(bigTextStyleReminderAppData.getPriority()) 210 .setVisibility(bigTextStyleReminderAppData.getChannelLockscreenVisibility()) 211 .addAction(snoozeAction) 212 .addAction(dismissAction); 213 214 /* REPLICATE_NOTIFICATION_STYLE_CODE: 215 * You can replicate Notification Style functionality on Wear 2.0 (24+) by not setting the 216 * main content intent, that is, skipping the call setContentIntent(). However, you need to 217 * still allow the user to open the native Wear app from the Notification itself, so you 218 * add an action to launch the app. 219 */ 220 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { 221 222 // Enables launching app in Wear 2.0 while keeping the old Notification Style behavior. 223 NotificationCompat.Action mainAction = new NotificationCompat.Action.Builder( 224 R.drawable.ic_launcher, 225 "Open", 226 mainPendingIntent) 227 .build(); 228 229 notificationCompatBuilder.addAction(mainAction); 230 231 } else { 232 // Wear 1.+ still functions the same, so we set the main content intent. 233 notificationCompatBuilder.setContentIntent(mainPendingIntent); 234 } 235 236 return notificationCompatBuilder; 237 } 238 }