• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 com.android.notification.functional;
18 
19 import android.app.Instrumentation;
20 import android.app.IntentService;
21 import android.app.KeyguardManager;
22 import android.app.Notification;
23 import android.app.NotificationChannel;
24 import android.app.NotificationManager;
25 import android.app.PendingIntent;
26 import android.app.RemoteInput;
27 import android.content.Context;
28 import android.content.Intent;
29 import android.graphics.Typeface;
30 import android.net.Uri;
31 import android.os.Handler;
32 import android.os.RemoteException;
33 import android.provider.Settings;
34 import android.service.notification.StatusBarNotification;
35 import android.support.test.uiautomator.By;
36 import android.support.test.uiautomator.UiDevice;
37 import android.support.test.uiautomator.UiObject;
38 import android.support.test.uiautomator.UiObjectNotFoundException;
39 import android.support.test.uiautomator.UiScrollable;
40 import android.support.test.uiautomator.UiSelector;
41 import android.support.test.uiautomator.Until;
42 import android.text.SpannableStringBuilder;
43 import android.text.style.StyleSpan;
44 import android.util.Log;
45 import android.widget.EditText;
46 import android.widget.ListView;
47 import android.widget.TextView;
48 import android.widget.Toast;
49 
50 import com.android.notification.functional.R;
51 
52 import java.lang.InterruptedException;
53 import java.util.List;
54 import java.util.Map;
55 
56 
57 public class NotificationHelper {
58 
59     private static final String LOG_TAG = NotificationHelper.class.getSimpleName();
60     private static final int LONG_TIMEOUT = 2500;
61     private static final int SHORT_TIMEOUT = 200;
62     private static final String KEY_QUICK_REPLY_TEXT = "quick_reply";
63     private static final UiSelector LIST_VIEW = new UiSelector().className(ListView.class);
64     private static final UiSelector LIST_ITEM_VALUE = new UiSelector().className(TextView.class);
65     public static final String FIRST_ACTION = "FIRST ACTION";
66     public static final String SECOND_ACTION = "SECOND ACTION";
67     public static final String CONTENT_TITLE = "THIS IS A NOTIFICATION";
68 
69     private UiDevice mDevice;
70     private Instrumentation mInst;
71     private NotificationManager mNotificationManager = null;
72     private Context mContext = null;
73 
NotificationHelper(UiDevice device, Instrumentation inst, NotificationManager nm)74     public NotificationHelper(UiDevice device, Instrumentation inst, NotificationManager nm) {
75         this.mDevice = device;
76         mInst = inst;
77         mNotificationManager = nm;
78         mContext = inst.getContext();
79     }
80 
sleepAndWakeUpDevice()81     public void sleepAndWakeUpDevice() throws RemoteException, InterruptedException {
82         mDevice.sleep();
83         Thread.sleep(LONG_TIMEOUT);
84         mDevice.wakeUp();
85     }
86 
launchSettingsPage(Context ctx, String pageName)87     public static void launchSettingsPage(Context ctx, String pageName) throws Exception {
88         Intent intent = new Intent(pageName);
89         intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
90         ctx.startActivity(intent);
91         Thread.sleep(LONG_TIMEOUT * 2);
92     }
93 
94     /**
95      * Sets the screen lock pin
96      * @param pin 4 digits
97      * @return false if a pin is already set or pin value is not 4 digits
98      * @throws UiObjectNotFoundException
99      */
setScreenLockPin(int pin)100     public boolean setScreenLockPin(int pin) throws Exception {
101         if (pin >= 0 && pin <= 9999) {
102             navigateToScreenLock();
103             if (new UiObject(new UiSelector().text("Confirm your PIN")).exists()) {
104                 UiObject pinField = new UiObject(
105                         new UiSelector().className(EditText.class.getName()));
106                 pinField.setText(String.format("%04d", pin));
107                 mDevice.pressEnter();
108             }
109             new UiObject(new UiSelector().text("PIN")).click();
110             // If there's an option to set 'require PIN to start device'
111             // choose 'No thanks', otherwise just skip ahead.
112             if (new UiObject(new UiSelector().text("No thanks")).exists()) {
113                 clickText("No thanks");
114             }
115             UiObject pinField = new UiObject(new UiSelector().className(EditText.class.getName()));
116             pinField.setText(String.format("%04d", pin));
117             mDevice.pressEnter();
118             pinField.setText(String.format("%04d", pin));
119             mDevice.pressEnter();
120             clickText("Hide sensitive notification content");
121             clickText("DONE");
122             return true;
123         }
124         return false;
125     }
126 
removeScreenLock(int pin, String mode)127     public boolean removeScreenLock(int pin, String mode) throws Exception {
128         navigateToScreenLock();
129         if (new UiObject(new UiSelector().text("Confirm your PIN")).exists()) {
130             UiObject pinField = new UiObject(new UiSelector().className(EditText.class.getName()));
131             pinField.setText(String.format("%04d", pin));
132             mDevice.pressEnter();
133             clickText(mode);
134             clickText("YES, REMOVE");
135         } else {
136             clickText(mode);
137         }
138         return true;
139     }
140 
unlockScreenByPin(int pin)141     public void unlockScreenByPin(int pin) throws Exception {
142         String command = String.format(" %s %s %s", "input", "text", Integer.toString(pin));
143         executeAdbCommand(command);
144         Thread.sleep(SHORT_TIMEOUT);
145         mDevice.pressEnter();
146     }
147 
enableNotificationViaAdb(boolean isShow)148     public void enableNotificationViaAdb(boolean isShow) {
149         String command = String.format(" %s %s %s %s %s", "settings", "put", "secure",
150                 "lock_screen_show_notifications",
151                 isShow ? "1" : "0");
152         executeAdbCommand(command);
153     }
154 
executeAdbCommand(String command)155     public void executeAdbCommand(String command) {
156         Log.i(LOG_TAG, String.format("executing - %s", command));
157         mInst.getUiAutomation().executeShellCommand(command);
158         mDevice.waitForIdle();
159     }
160 
navigateToScreenLock()161     private void navigateToScreenLock() throws Exception {
162         launchSettingsPage(mInst.getContext(), Settings.ACTION_SECURITY_SETTINGS);
163         new UiObject(new UiSelector().text("Screen lock")).click();
164     }
165 
clickText(String text)166     private void clickText(String text) throws UiObjectNotFoundException {
167         mDevice.wait(Until.findObject(By.text(text)), LONG_TIMEOUT).click();
168     }
169 
sendNotification(int id, int visibility, String title)170     public void sendNotification(int id, int visibility, String title) throws Exception {
171         sendNotification(id, visibility, title, false);
172     }
173 
sendNotification(int id, int visibility, String title, boolean buzz)174     public void sendNotification(int id, int visibility, String title, boolean buzz)
175             throws Exception {
176         Log.v(LOG_TAG, "Sending out notification...");
177         PendingIntent emptyIntent = PendingIntent.getBroadcast(mContext, 0,
178                 new Intent("an.action.that.nobody.will.be.listening.for"), 0);
179         Intent intent = new Intent(Intent.ACTION_VIEW);
180         PendingIntent pendingIntent = PendingIntent.getActivity(mContext, 0, intent, 0);
181         CharSequence subtitle = String.valueOf(System.currentTimeMillis());
182         Notification.Builder notification = new Notification.Builder(mContext)
183                 .setSmallIcon(R.drawable.stat_notify_email)
184                 .setWhen(System.currentTimeMillis())
185                 .setContentTitle(title)
186                 .setContentText(subtitle)
187                 .setContentIntent(pendingIntent)
188                 .setVisibility(visibility)
189                 .setPriority(Notification.PRIORITY_HIGH)
190                 .addAction(new Notification.Action.Builder(R.drawable.stat_notify_email,
191                         FIRST_ACTION, emptyIntent)
192                         .build())
193                 .addAction(new Notification.Action.Builder(R.drawable.stat_notify_email,
194                         SECOND_ACTION, emptyIntent)
195                         .build())
196                 .setAutoCancel(false);
197         if (buzz) {
198             notification.setDefaults(Notification.DEFAULT_VIBRATE);
199         }
200         mNotificationManager.notify(id, notification.build());
201         Thread.sleep(LONG_TIMEOUT);
202     }
203 
sendNotifications(Map<Integer, String> lists, boolean withDelay)204     public void sendNotifications(Map<Integer, String> lists, boolean withDelay) throws Exception {
205         Log.v(LOG_TAG, "Sending out notification...");
206         CharSequence subtitle = String.valueOf(System.currentTimeMillis());
207         for (Map.Entry<Integer, String> l : lists.entrySet()) {
208             Notification.Builder notification = new Notification.Builder(mContext)
209                     .setSmallIcon(R.drawable.stat_notify_email)
210                     .setWhen(System.currentTimeMillis()).setContentTitle(l.getValue())
211                     .setContentTitle(CONTENT_TITLE)
212                     .setContentText(subtitle);
213             mNotificationManager.notify(l.getKey(), notification.build());
214             if (withDelay) {
215                 Thread.sleep(SHORT_TIMEOUT);
216             }
217         }
218         Thread.sleep(LONG_TIMEOUT);
219     }
220 
sendBundlingNotifications(List<Integer> lists, String groupKey)221     public void sendBundlingNotifications(List<Integer> lists, String groupKey) throws Exception {
222         Notification childNotification = new Notification.Builder(mContext)
223                 .setContentTitle(lists.get(1).toString())
224                 .setSmallIcon(R.drawable.stat_notify_email)
225                 .setGroup(groupKey)
226                 .build();
227         mNotificationManager.notify(lists.get(1),
228                 childNotification);
229         childNotification = new Notification.Builder(mContext)
230                 .setContentText(lists.get(2).toString())
231                 .setSmallIcon(R.drawable.stat_notify_email)
232                 .setGroup(groupKey)
233                 .build();
234         mNotificationManager.notify(lists.get(2),
235                 childNotification);
236         Notification notification = new Notification.Builder(mContext)
237                 .setContentTitle(lists.get(0).toString())
238                 .setSubText(groupKey)
239                 .setSmallIcon(R.drawable.stat_notify_email)
240                 .setGroup(groupKey)
241                 .setGroupSummary(true)
242                 .build();
243         mNotificationManager.notify(lists.get(0),
244                 notification);
245     }
246 
BOLD(CharSequence str)247     static SpannableStringBuilder BOLD(CharSequence str) {
248         final SpannableStringBuilder ssb = new SpannableStringBuilder(str);
249         ssb.setSpan(new StyleSpan(Typeface.BOLD), 0, ssb.length(), 0);
250         return ssb;
251     }
252 
checkNotificationExistence(int id, boolean exists)253     public boolean checkNotificationExistence(int id, boolean exists) throws Exception {
254         boolean isFound = false;
255         for (int tries = 3; tries-- > 0;) {
256             isFound = false;
257             StatusBarNotification[] sbns = mNotificationManager.getActiveNotifications();
258             for (StatusBarNotification sbn : sbns) {
259                 if (sbn.getId() == id) {
260                     isFound = true;
261                     break;
262                 }
263             }
264             if (isFound == exists) {
265                 break;
266             }
267             Thread.sleep(SHORT_TIMEOUT);
268         }
269         Log.i(LOG_TAG, "checkNotificationExistence..." + isFound);
270         return isFound == exists;
271     }
272 
getStatusBarNotification(int id)273     public StatusBarNotification getStatusBarNotification(int id) {
274         StatusBarNotification[] sbns = mNotificationManager.getActiveNotifications();
275         StatusBarNotification n = null;
276         for (StatusBarNotification sbn : sbns) {
277             if (sbn.getId() == id) {
278                 n = sbn;
279                 break;
280             }
281         }
282         return n;
283     }
284 
swipeUp()285     public void swipeUp() throws Exception {
286         mDevice.swipe(mDevice.getDisplayWidth() / 2, mDevice.getDisplayHeight()*3/4,
287                 mDevice.getDisplayWidth() / 2, 0, 30);
288         Thread.sleep(SHORT_TIMEOUT);
289     }
290 
swipeDown()291     public void swipeDown() throws Exception {
292         mDevice.swipe(mDevice.getDisplayWidth() / 2, 0, mDevice.getDisplayWidth() / 2,
293                 mDevice.getDisplayHeight() / 2 + 50, 20);
294         Thread.sleep(SHORT_TIMEOUT);
295     }
296 
unlockScreen()297     public void unlockScreen() throws Exception {
298         KeyguardManager myKM = (KeyguardManager) mContext
299                 .getSystemService(Context.KEYGUARD_SERVICE);
300         if (myKM.inKeyguardRestrictedInputMode()) {
301             // it is locked
302             swipeUp();
303         }
304     }
305 
showAppNotificationSettings(Context context)306     public void showAppNotificationSettings(Context context) throws Exception {
307         Intent intent = new Intent(Settings.ACTION_APP_NOTIFICATION_SETTINGS);
308         intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
309         intent.putExtra(Settings.EXTRA_APP_PACKAGE, mContext.getPackageName());
310         intent.putExtra(Settings.EXTRA_APP_UID, mContext.getApplicationInfo().uid);
311         context.startActivity(intent);
312         Thread.sleep(LONG_TIMEOUT * 2);
313     }
314 
315     /**
316      * This is the main list view containing the items that settings are possible for
317      */
318     public static class SettingsListView {
selectSettingsFor(String name)319         public static boolean selectSettingsFor(String name) throws UiObjectNotFoundException {
320             UiScrollable settingsList = new UiScrollable(
321                     new UiSelector().resourceId("android:id/content"));
322             UiObject appSettings = settingsList.getChildByText(LIST_ITEM_VALUE, name);
323             if (appSettings != null) {
324                 return appSettings.click();
325             }
326             return false;
327         }
328 
checkSettingsExists(String name)329         public boolean checkSettingsExists(String name) {
330             try {
331                 UiScrollable settingsList = new UiScrollable(LIST_VIEW);
332                 UiObject appSettings = settingsList.getChildByText(LIST_ITEM_VALUE, name);
333                 return appSettings.exists();
334             } catch (UiObjectNotFoundException e) {
335                 return false;
336             }
337         }
338     }
339 
sendNotificationsWithInLineReply(int notificationId, boolean isHeadsUp)340     public void sendNotificationsWithInLineReply(int notificationId, boolean isHeadsUp) {
341         Notification.Action action = new Notification.Action.Builder(
342                 R.drawable.stat_notify_email, "Reply", ToastService.getPendingIntent(mContext,
343                         "inline reply test"))
344                                 .addRemoteInput(new RemoteInput.Builder(KEY_QUICK_REPLY_TEXT)
345                                         .setLabel("Quick reply").build())
346                                 .build();
347         Notification.Builder n = new Notification.Builder(mContext)
348                 .setContentTitle(Integer.toString(notificationId))
349                 .setContentText("INLINE REPLY TEST")
350                 .setWhen(System.currentTimeMillis())
351                 .setSmallIcon(R.drawable.stat_notify_email)
352                 .addAction(action);
353         if (isHeadsUp) {
354             n.setPriority(Notification.PRIORITY_HIGH)
355                     .setDefaults(Notification.DEFAULT_VIBRATE);
356         }
357         mNotificationManager.notify(notificationId, n.build());
358     }
359 
getDefaultChannel()360     public NotificationChannel getDefaultChannel() {
361         return mNotificationManager.getNotificationChannel(NotificationChannel.DEFAULT_CHANNEL_ID);
362     }
363 
364     public static class ToastService extends IntentService {
365         private static final String TAG = "ToastService";
366         private static final String ACTION_TOAST = "toast";
367         private Handler handler;
368 
ToastService()369         public ToastService() {
370             super(TAG);
371         }
372 
ToastService(String name)373         public ToastService(String name) {
374             super(name);
375         }
376 
377         @Override
onStartCommand(Intent intent, int flags, int startId)378         public int onStartCommand(Intent intent, int flags, int startId) {
379             handler = new Handler();
380             return super.onStartCommand(intent, flags, startId);
381         }
382 
383         @Override
onHandleIntent(Intent intent)384         protected void onHandleIntent(Intent intent) {
385             if (intent.hasExtra("text")) {
386                 final String text = intent.getStringExtra("text");
387                 handler.post(new Runnable() {
388                     @Override
389                     public void run() {
390                         Toast.makeText(ToastService.this, text, Toast.LENGTH_LONG).show();
391                         Log.v(TAG, "toast " + text);
392                     }
393                 });
394             }
395         }
396 
getPendingIntent(Context context, String text)397         public static PendingIntent getPendingIntent(Context context, String text) {
398             Intent toastIntent = new Intent(context, ToastService.class);
399             toastIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
400             toastIntent.setAction(ACTION_TOAST + ":" + text); // one per toast message
401             toastIntent.putExtra("text", text);
402             PendingIntent pi = PendingIntent.getService(
403                     context, 58, toastIntent, PendingIntent.FLAG_UPDATE_CURRENT);
404             return pi;
405         }
406     }
407 }
408