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