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