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