1 /* 2 * Copyright (C) 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 17 package android.system.helpers; 18 19 import android.app.IntentService; 20 import android.app.Notification; 21 import android.app.NotificationChannel; 22 import android.app.NotificationManager; 23 import android.app.PendingIntent; 24 import android.app.RemoteInput; 25 import android.content.Context; 26 import android.content.Intent; 27 import android.os.Handler; 28 import android.service.notification.StatusBarNotification; 29 import android.support.test.InstrumentationRegistry; 30 import android.support.test.uiautomator.UiDevice; 31 import android.util.Log; 32 import android.widget.Toast; 33 34 import java.util.List; 35 36 /** 37 * Implement common helper methods for Notification. 38 */ 39 public class NotificationHelper { 40 private static final String LOG_TAG = NotificationHelper.class.getSimpleName(); 41 public static final int SHORT_TIMEOUT = 200; 42 public static final int LONG_TIMEOUT = 2000; 43 private static NotificationHelper sInstance = null; 44 private Context mContext = null; 45 private UiDevice mDevice = null; 46 private String mCurrentChannelId = NotificationChannel.DEFAULT_CHANNEL_ID; 47 48 public NotificationHelper() { 49 mContext = InstrumentationRegistry.getTargetContext(); 50 mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()); 51 } 52 53 public static NotificationHelper getInstance() { 54 if (sInstance == null) { 55 sInstance = new NotificationHelper(); 56 } 57 return sInstance; 58 } 59 60 /** 61 * Creates a notification channel if none exists. 62 * @param id channel id, must be unique 63 * @param name channel name 64 * @param importance channel importance 65 * @param vibrate vibrate on notification 66 * @param manager the {@link NotificationManager} 67 * @throws IllegalArgumentException if the channel id is already in use. 68 */ 69 public void createChannel(String id, String name, int importance, boolean vibrate, 70 NotificationManager manager) { 71 if (manager.getNotificationChannel(id) != null) { 72 throw new IllegalArgumentException("Channel already exists."); 73 } 74 NotificationChannel channel = new NotificationChannel(id, name, importance); 75 channel.enableVibration(vibrate); 76 channel.setSound(null, null); 77 manager.createNotificationChannel(channel); 78 } 79 80 /** 81 * Uses this notification channel when using this helper. 82 * @param id the channel id to use 83 */ 84 public void useChannel(String id) { 85 mCurrentChannelId = id; 86 } 87 88 /** 89 * Check if a list of notifications exist. 90 * @param ids list of notification ids 91 * @param manager the {@link NotificationManager} 92 * @return true if all notifications exist, false otherwise 93 * @throws InterruptedException if the running thread is interrupted. 94 */ 95 public boolean checkNotificationExistence(List<Integer> ids, NotificationManager manager) 96 throws InterruptedException { 97 boolean result = true; 98 for (int id : ids) { 99 result = result && checkNotificationExistence(id, manager); 100 } 101 return result; 102 } 103 104 /** 105 * Check if a notification exists. 106 * @param id notification id 107 * @param manager the {@link NotificationManager} 108 * @return true if all notifications exist, false otherwise 109 * @throws InterruptedException if the running thread is interrupted. 110 */ 111 public boolean checkNotificationExistence(int id, NotificationManager manager) 112 throws InterruptedException { 113 boolean isFound = false; 114 for (int tries = 3; tries-- > 0;) { 115 isFound = false; 116 StatusBarNotification[] sbns = manager.getActiveNotifications(); 117 for (StatusBarNotification sbn : sbns) { 118 if (sbn.getId() == id) { 119 isFound = true; 120 break; 121 } 122 } 123 if (isFound) { 124 break; 125 } 126 Thread.sleep(SHORT_TIMEOUT); 127 } 128 Log.i(LOG_TAG, String.format("Notification (id=%d) existence = %b", id, isFound)); 129 return isFound; 130 } 131 132 /** 133 * send out a group of notifications 134 * @param lists notification list for a group of notifications which includes two child 135 * notifications and one summary notification 136 * @param groupKey the group key of group notification 137 * @param mNotificationManager NotificationManager 138 * @throws Exception 139 */ 140 public void sendBundlingNotifications(List<Integer> lists, String groupKey, 141 NotificationManager mNotificationManager) throws Exception { 142 Notification childNotification = new Notification.Builder(mContext) 143 .setChannelId(mCurrentChannelId) 144 .setContentTitle(lists.get(1).toString()) 145 .setSmallIcon(android.R.drawable.stat_notify_chat) 146 .setContentText("test1") 147 .setWhen(System.currentTimeMillis()) 148 .setGroup(groupKey) 149 .build(); 150 mNotificationManager.notify(lists.get(1), 151 childNotification); 152 childNotification = new Notification.Builder(mContext) 153 .setChannelId(mCurrentChannelId) 154 .setContentTitle(lists.get(2).toString()) 155 .setContentText("test2") 156 .setSmallIcon(android.R.drawable.stat_notify_chat) 157 .setWhen(System.currentTimeMillis()) 158 .setGroup(groupKey) 159 .build(); 160 mNotificationManager.notify(lists.get(2), 161 childNotification); 162 Notification notification = new Notification.Builder(mContext) 163 .setChannelId(mCurrentChannelId) 164 .setContentTitle(lists.get(0).toString()) 165 .setSubText(groupKey) 166 .setSmallIcon(android.R.drawable.stat_notify_chat) 167 .setGroup(groupKey) 168 .setGroupSummary(true) 169 .build(); 170 mNotificationManager.notify(lists.get(0), 171 notification); 172 } 173 174 /** 175 * send out a notification with inline reply 176 * @param notificationId An identifier for this notification 177 * @param title notification title 178 * @param inLineReply inline reply text 179 * @param mNotificationManager NotificationManager 180 */ 181 public void sendNotificationsWithInLineReply( 182 int notificationId, String title, String inLineReply, PendingIntent pendingIntent, 183 NotificationManager mNotificationManager) { 184 Notification.Action action = new Notification.Action.Builder( 185 android.R.drawable.stat_notify_chat, "Reply", 186 pendingIntent).addRemoteInput(new RemoteInput.Builder(inLineReply) 187 .setLabel("Quick reply").build()) 188 .build(); 189 Notification.Builder n = new Notification.Builder(mContext) 190 .setChannelId(mCurrentChannelId) 191 .setContentTitle(Integer.toString(notificationId)) 192 .setContentText(title) 193 .setWhen(System.currentTimeMillis()) 194 .setSmallIcon(android.R.drawable.stat_notify_chat) 195 .addAction(action) 196 .setDefaults(Notification.DEFAULT_VIBRATE); 197 mNotificationManager.notify(notificationId, n.build()); 198 } 199 200 /** 201 * dismiss notification 202 * @param mNotificationManager NotificationManager 203 */ 204 public void dismissNotifications(NotificationManager mNotificationManager){ 205 mNotificationManager.cancelAll(); 206 } 207 208 /** 209 * open notification shade 210 */ 211 public void openNotification(){ 212 mDevice.openNotification(); 213 } 214 215 /** 216 * An {@link IntentService} for creating pending intents that can be used with to send 217 * notifications with inline reply text content. 218 */ 219 public static class ToastService extends IntentService { 220 private Handler mHandler; 221 222 public ToastService() { 223 super("Toast Service"); 224 } 225 226 @Override 227 public int onStartCommand(Intent intent, int flags, int startId) { 228 return super.onStartCommand(intent, flags, startId); 229 } 230 231 @Override 232 protected void onHandleIntent(Intent intent) { 233 if (mHandler == null) { 234 mHandler = new Handler(); 235 } 236 if (intent.hasExtra("text")) { 237 mHandler.post(new Runnable() { 238 @Override 239 public void run() { 240 Toast.makeText( 241 ToastService.this, intent.getStringExtra("text"), Toast.LENGTH_LONG) 242 .show(); 243 } 244 }); 245 } 246 } 247 248 /** 249 * Returns a {@link PendingIntent} for a Toast message. 250 */ 251 public static PendingIntent getPendingIntent(Context context, String text) { 252 Intent toastIntent = new Intent(context, ToastService.class); 253 toastIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 254 toastIntent.setAction("toast:" + text); 255 toastIntent.putExtra("text", text); 256 PendingIntent pi = PendingIntent.getService( 257 context, 58, toastIntent, PendingIntent.FLAG_UPDATE_CURRENT); 258 return pi; 259 } 260 } 261 } 262