/* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.system.helpers; import android.app.IntentService; import android.app.Notification; import android.app.NotificationChannel; import android.app.NotificationManager; import android.app.PendingIntent; import android.app.RemoteInput; import android.content.Context; import android.content.Intent; import android.os.Handler; import android.service.notification.StatusBarNotification; import android.support.test.uiautomator.UiDevice; import android.util.Log; import android.widget.Toast; import androidx.test.InstrumentationRegistry; import java.util.List; /** * Implement common helper methods for Notification. */ public class NotificationHelper { private static final String LOG_TAG = NotificationHelper.class.getSimpleName(); public static final int SHORT_TIMEOUT = 200; public static final int LONG_TIMEOUT = 2000; private static NotificationHelper sInstance = null; private Context mContext = null; private UiDevice mDevice = null; private String mCurrentChannelId = NotificationChannel.DEFAULT_CHANNEL_ID; public NotificationHelper() { mContext = InstrumentationRegistry.getTargetContext(); mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()); } public static NotificationHelper getInstance() { if (sInstance == null) { sInstance = new NotificationHelper(); } return sInstance; } /** * Creates a notification channel if none exists. * @param id channel id, must be unique * @param name channel name * @param importance channel importance * @param vibrate vibrate on notification * @param manager the {@link NotificationManager} * @throws IllegalArgumentException if the channel id is already in use. */ public void createChannel(String id, String name, int importance, boolean vibrate, NotificationManager manager) { if (manager.getNotificationChannel(id) != null) { throw new IllegalArgumentException("Channel already exists."); } NotificationChannel channel = new NotificationChannel(id, name, importance); channel.enableVibration(vibrate); channel.setSound(null, null); manager.createNotificationChannel(channel); } /** * Uses this notification channel when using this helper. * @param id the channel id to use */ public void useChannel(String id) { mCurrentChannelId = id; } /** * Check if a list of notifications exist. * @param ids list of notification ids * @param manager the {@link NotificationManager} * @return true if all notifications exist, false otherwise * @throws InterruptedException if the running thread is interrupted. */ public boolean checkNotificationExistence(List ids, NotificationManager manager) throws InterruptedException { boolean result = true; for (int id : ids) { result = result && checkNotificationExistence(id, manager); } return result; } /** * Check if a notification exists. * @param id notification id * @param manager the {@link NotificationManager} * @return true if all notifications exist, false otherwise * @throws InterruptedException if the running thread is interrupted. */ public boolean checkNotificationExistence(int id, NotificationManager manager) throws InterruptedException { boolean isFound = false; for (int tries = 3; tries-- > 0;) { isFound = false; StatusBarNotification[] sbns = manager.getActiveNotifications(); for (StatusBarNotification sbn : sbns) { if (sbn.getId() == id) { isFound = true; break; } } if (isFound) { break; } Thread.sleep(SHORT_TIMEOUT); } Log.i(LOG_TAG, String.format("Notification (id=%d) existence = %b", id, isFound)); return isFound; } /** * send out a group of notifications * @param lists notification list for a group of notifications which includes two child * notifications and one summary notification * @param groupKey the group key of group notification * @param mNotificationManager NotificationManager * @throws Exception */ public void sendBundlingNotifications(List lists, String groupKey, NotificationManager mNotificationManager) throws Exception { Notification childNotification = new Notification.Builder(mContext) .setChannelId(mCurrentChannelId) .setContentTitle(lists.get(1).toString()) .setSmallIcon(android.R.drawable.stat_notify_chat) .setContentText("test1") .setWhen(System.currentTimeMillis()) .setGroup(groupKey) .build(); mNotificationManager.notify(lists.get(1), childNotification); childNotification = new Notification.Builder(mContext) .setChannelId(mCurrentChannelId) .setContentTitle(lists.get(2).toString()) .setContentText("test2") .setSmallIcon(android.R.drawable.stat_notify_chat) .setWhen(System.currentTimeMillis()) .setGroup(groupKey) .build(); mNotificationManager.notify(lists.get(2), childNotification); Notification notification = new Notification.Builder(mContext) .setChannelId(mCurrentChannelId) .setContentTitle(lists.get(0).toString()) .setSubText(groupKey) .setSmallIcon(android.R.drawable.stat_notify_chat) .setGroup(groupKey) .setGroupSummary(true) .build(); mNotificationManager.notify(lists.get(0), notification); } /** * send out a notification with inline reply * @param notificationId An identifier for this notification * @param title notification title * @param inLineReply inline reply text * @param mNotificationManager NotificationManager */ public void sendNotificationsWithInLineReply( int notificationId, String title, String inLineReply, PendingIntent pendingIntent, NotificationManager mNotificationManager) { Notification.Action action = new Notification.Action.Builder( android.R.drawable.stat_notify_chat, "Reply", pendingIntent).addRemoteInput(new RemoteInput.Builder(inLineReply) .setLabel("Quick reply").build()) .build(); Notification.Builder n = new Notification.Builder(mContext) .setChannelId(mCurrentChannelId) .setContentTitle(Integer.toString(notificationId)) .setContentText(title) .setWhen(System.currentTimeMillis()) .setSmallIcon(android.R.drawable.stat_notify_chat) .addAction(action) .setDefaults(Notification.DEFAULT_VIBRATE); mNotificationManager.notify(notificationId, n.build()); } /** * dismiss notification * @param mNotificationManager NotificationManager */ public void dismissNotifications(NotificationManager mNotificationManager){ mNotificationManager.cancelAll(); } /** * open notification shade */ public void openNotification(){ mDevice.openNotification(); } /** * An {@link IntentService} for creating pending intents that can be used with to send * notifications with inline reply text content. */ public static class ToastService extends IntentService { private Handler mHandler; public ToastService() { super("Toast Service"); } @Override public int onStartCommand(Intent intent, int flags, int startId) { return super.onStartCommand(intent, flags, startId); } @Override protected void onHandleIntent(Intent intent) { if (mHandler == null) { mHandler = new Handler(); } if (intent.hasExtra("text")) { mHandler.post(new Runnable() { @Override public void run() { Toast.makeText( ToastService.this, intent.getStringExtra("text"), Toast.LENGTH_LONG) .show(); } }); } } /** * Returns a {@link PendingIntent} for a Toast message. */ public static PendingIntent getPendingIntent(Context context, String text) { Intent toastIntent = new Intent(context, ToastService.class); toastIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); toastIntent.setAction("toast:" + text); toastIntent.putExtra("text", text); PendingIntent pi = PendingIntent.getService( context, 58, toastIntent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE_UNAUDITED); return pi; } } }