1 /* 2 * Copyright (C) 2015 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.app; 18 19 import android.content.ContentResolver; 20 import android.content.Context; 21 import android.content.Intent; 22 import android.database.Cursor; 23 import android.graphics.Bitmap; 24 import android.graphics.Canvas; 25 import android.graphics.Typeface; 26 import android.graphics.drawable.BitmapDrawable; 27 import android.graphics.drawable.Drawable; 28 import android.net.Uri; 29 import android.os.Bundle; 30 import android.os.Handler; 31 import android.os.Parcel; 32 import android.os.SystemClock; 33 import android.provider.ContactsContract; 34 import android.test.AndroidTestCase; 35 import android.test.suitebuilder.annotation.Suppress; 36 import android.text.SpannableStringBuilder; 37 import android.text.TextUtils; 38 import android.text.style.StyleSpan; 39 import android.util.Log; 40 import android.view.View; 41 import android.widget.Toast; 42 43 import java.lang.reflect.Method; 44 import java.lang.InterruptedException; 45 import java.lang.NoSuchMethodError; 46 import java.lang.NoSuchMethodException; 47 import java.util.ArrayList; 48 49 import com.android.frameworks.tests.notification.R; 50 51 public class NotificationTests extends AndroidTestCase { 52 private static final String TAG = "NOTEST"; L(String msg, Object... args)53 public static void L(String msg, Object... args) { 54 Log.v(TAG, (args == null || args.length == 0) ? msg : String.format(msg, args)); 55 } 56 57 public static final String ACTION_CREATE = "create"; 58 public static final int NOTIFICATION_ID = 31338; 59 60 public static final boolean SHOW_PHONE_CALL = false; 61 public static final boolean SHOW_INBOX = true; 62 public static final boolean SHOW_BIG_TEXT = true; 63 public static final boolean SHOW_BIG_PICTURE = true; 64 public static final boolean SHOW_MEDIA = true; 65 public static final boolean SHOW_STOPWATCH = false; 66 public static final boolean SHOW_SOCIAL = false; 67 public static final boolean SHOW_CALENDAR = false; 68 public static final boolean SHOW_PROGRESS = false; 69 getBitmap(Context context, int resId)70 private static Bitmap getBitmap(Context context, int resId) { 71 int largeIconWidth = (int) context.getResources() 72 .getDimension(R.dimen.notification_large_icon_width); 73 int largeIconHeight = (int) context.getResources() 74 .getDimension(R.dimen.notification_large_icon_height); 75 Drawable d = context.getResources().getDrawable(resId); 76 Bitmap b = Bitmap.createBitmap(largeIconWidth, largeIconHeight, Bitmap.Config.ARGB_8888); 77 Canvas c = new Canvas(b); 78 d.setBounds(0, 0, largeIconWidth, largeIconHeight); 79 d.draw(c); 80 return b; 81 } 82 makeEmailIntent(Context context, String who)83 private static PendingIntent makeEmailIntent(Context context, String who) { 84 final Intent intent = new Intent(android.content.Intent.ACTION_SENDTO, 85 Uri.parse("mailto:" + who)); 86 return PendingIntent.getActivity( 87 context, 0, intent, 88 PendingIntent.FLAG_CANCEL_CURRENT); 89 } 90 91 static final String[] LINES = new String[] { 92 "Uh oh", 93 "Getting kicked out of this room", 94 "I'll be back in 5-10 minutes.", 95 "And now \u2026 I have to find my shoes. \uD83D\uDC63", 96 "\uD83D\uDC5F \uD83D\uDC5F", 97 "\uD83D\uDC60 \uD83D\uDC60", 98 }; 99 static final int MAX_LINES = 5; makeBigTextNotification(Context context, int update, int id, long when)100 public static Notification makeBigTextNotification(Context context, int update, int id, 101 long when) { 102 String personUri = null; 103 /* 104 Cursor c = null; 105 try { 106 String[] projection = new String[] { ContactsContract.Contacts._ID, ContactsContract.Contacts.LOOKUP_KEY }; 107 String selections = ContactsContract.Contacts.DISPLAY_NAME + " = 'Mike Cleron'"; 108 final ContentResolver contentResolver = context.getContentResolver(); 109 c = contentResolver.query(ContactsContract.Contacts.CONTENT_URI, 110 projection, selections, null, null); 111 if (c != null && c.getCount() > 0) { 112 c.moveToFirst(); 113 int lookupIdx = c.getColumnIndex(ContactsContract.Contacts.LOOKUP_KEY); 114 int idIdx = c.getColumnIndex(ContactsContract.Contacts._ID); 115 String lookupKey = c.getString(lookupIdx); 116 long contactId = c.getLong(idIdx); 117 Uri lookupUri = ContactsContract.Contacts.getLookupUri(contactId, lookupKey); 118 personUri = lookupUri.toString(); 119 } 120 } finally { 121 if (c != null) { 122 c.close(); 123 } 124 } 125 if (TextUtils.isEmpty(personUri)) { 126 Log.w(TAG, "failed to find contact for Mike Cleron"); 127 } else { 128 Log.w(TAG, "Mike Cleron is " + personUri); 129 } 130 */ 131 132 StringBuilder longSmsText = new StringBuilder(); 133 int end = 2 + update; 134 if (end > LINES.length) { 135 end = LINES.length; 136 } 137 final int start = Math.max(0, end - MAX_LINES); 138 for (int i=start; i<end; i++) { 139 if (i >= LINES.length) break; 140 if (i > start) longSmsText.append("\n"); 141 longSmsText.append(LINES[i]); 142 } 143 if (update > 2) { 144 when = System.currentTimeMillis(); 145 } 146 Notification.BigTextStyle bigTextStyle = new Notification.BigTextStyle() 147 .bigText(longSmsText); 148 Notification bigText = new Notification.Builder(context) 149 .setContentTitle("Mike Cleron") 150 .setContentIntent(ToastService.getPendingIntent(context, "Clicked on bigText")) 151 .setContentText(longSmsText) 152 //.setTicker("Mike Cleron: " + longSmsText) 153 .setWhen(when) 154 .setLargeIcon(getBitmap(context, R.drawable.bucket)) 155 .setPriority(Notification.PRIORITY_HIGH) 156 .setNumber(update) 157 .setSmallIcon(R.drawable.stat_notify_talk_text) 158 .setStyle(bigTextStyle) 159 .setDefaults(Notification.DEFAULT_SOUND) 160 .addPerson(personUri) 161 .build(); 162 return bigText; 163 } 164 makeUploadNotification(Context context, int progress, long when)165 public static Notification makeUploadNotification(Context context, int progress, long when) { 166 Notification.Builder uploadNotification = new Notification.Builder(context) 167 .setContentTitle("File Upload") 168 .setContentText("foo.txt") 169 .setPriority(Notification.PRIORITY_MIN) 170 .setContentIntent(ToastService.getPendingIntent(context, "Clicked on Upload")) 171 .setWhen(when) 172 .setSmallIcon(R.drawable.ic_menu_upload) 173 .setProgress(100, Math.min(progress, 100), false); 174 return uploadNotification.build(); 175 } 176 BOLD(CharSequence str)177 static SpannableStringBuilder BOLD(CharSequence str) { 178 final SpannableStringBuilder ssb = new SpannableStringBuilder(str); 179 ssb.setSpan(new StyleSpan(Typeface.BOLD), 0, ssb.length(), 0); 180 return ssb; 181 } 182 183 public static class ToastService extends IntentService { 184 185 private static final String TAG = "ToastService"; 186 187 private static final String ACTION_TOAST = "toast"; 188 189 private Handler handler; 190 ToastService()191 public ToastService() { 192 super(TAG); 193 } ToastService(String name)194 public ToastService(String name) { 195 super(name); 196 } 197 198 @Override onStartCommand(Intent intent, int flags, int startId)199 public int onStartCommand(Intent intent, int flags, int startId) { 200 handler = new Handler(); 201 return super.onStartCommand(intent, flags, startId); 202 } 203 204 @Override onHandleIntent(Intent intent)205 protected void onHandleIntent(Intent intent) { 206 Log.v(TAG, "clicked a thing! intent=" + intent.toString()); 207 if (intent.hasExtra("text")) { 208 final String text = intent.getStringExtra("text"); 209 handler.post(new Runnable() { 210 @Override 211 public void run() { 212 Toast.makeText(ToastService.this, text, Toast.LENGTH_LONG).show(); 213 Log.v(TAG, "toast " + text); 214 } 215 }); 216 } 217 } 218 getPendingIntent(Context context, String text)219 public static PendingIntent getPendingIntent(Context context, String text) { 220 Intent toastIntent = new Intent(context, ToastService.class); 221 toastIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 222 toastIntent.setAction(ACTION_TOAST + ":" + text); // one per toast message 223 toastIntent.putExtra("text", text); 224 PendingIntent pi = PendingIntent.getService( 225 context, 58, toastIntent, PendingIntent.FLAG_UPDATE_CURRENT); 226 return pi; 227 } 228 } 229 sleepIfYouCan(int ms)230 public static void sleepIfYouCan(int ms) { 231 try { 232 Thread.sleep(ms); 233 } catch (InterruptedException e) {} 234 } 235 236 @Override setUp()237 public void setUp() throws Exception { 238 super.setUp(); 239 } 240 summarize(Notification n)241 public static String summarize(Notification n) { 242 return String.format("<notif title=\"%s\" icon=0x%08x view=%s>", 243 n.extras.get(Notification.EXTRA_TITLE), 244 n.icon, 245 String.valueOf(n.contentView)); 246 } 247 testCreate()248 public void testCreate() throws Exception { 249 ArrayList<Notification> mNotifications = new ArrayList<Notification>(); 250 NotificationManager noMa = 251 (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE); 252 253 L("Constructing notifications..."); 254 if (SHOW_BIG_TEXT) { 255 int bigtextId = mNotifications.size(); 256 final long time = SystemClock.currentThreadTimeMillis(); 257 final Notification n = makeBigTextNotification(mContext, 0, bigtextId, System.currentTimeMillis()); 258 L(" %s: create=%dms", summarize(n), SystemClock.currentThreadTimeMillis() - time); 259 mNotifications.add(n); 260 } 261 262 int uploadId = mNotifications.size(); 263 long uploadWhen = System.currentTimeMillis(); 264 265 if (SHOW_PROGRESS) { 266 mNotifications.add(makeUploadNotification(mContext, 0, uploadWhen)); 267 } 268 269 if (SHOW_PHONE_CALL) { 270 int phoneId = mNotifications.size(); 271 final PendingIntent fullscreenIntent 272 = FullScreenActivity.getPendingIntent(mContext, phoneId); 273 final long time = SystemClock.currentThreadTimeMillis(); 274 Notification phoneCall = new Notification.Builder(mContext) 275 .setContentTitle("Incoming call") 276 .setContentText("Matias Duarte") 277 .setLargeIcon(getBitmap(mContext, R.drawable.matias_hed)) 278 .setSmallIcon(R.drawable.stat_sys_phone_call) 279 .setDefaults(Notification.DEFAULT_SOUND) 280 .setPriority(Notification.PRIORITY_MAX) 281 .setContentIntent(fullscreenIntent) 282 .setFullScreenIntent(fullscreenIntent, true) 283 .addAction(R.drawable.ic_dial_action_call, "Answer", 284 ToastService.getPendingIntent(mContext, "Clicked on Answer")) 285 .addAction(R.drawable.ic_end_call, "Ignore", 286 ToastService.getPendingIntent(mContext, "Clicked on Ignore")) 287 .setOngoing(true) 288 .addPerson(Uri.fromParts("tel", "1 (617) 555-1212", null).toString()) 289 .build(); 290 L(" %s: create=%dms", phoneCall.toString(), SystemClock.currentThreadTimeMillis() - time); 291 mNotifications.add(phoneCall); 292 } 293 294 if (SHOW_STOPWATCH) { 295 final long time = SystemClock.currentThreadTimeMillis(); 296 final Notification n = new Notification.Builder(mContext) 297 .setContentTitle("Stopwatch PRO") 298 .setContentText("Counting up") 299 .setContentIntent(ToastService.getPendingIntent(mContext, "Clicked on Stopwatch")) 300 .setSmallIcon(R.drawable.stat_notify_alarm) 301 .setUsesChronometer(true) 302 .build(); 303 L(" %s: create=%dms", summarize(n), SystemClock.currentThreadTimeMillis() - time); 304 mNotifications.add(n); 305 } 306 307 if (SHOW_CALENDAR) { 308 final long time = SystemClock.currentThreadTimeMillis(); 309 final Notification n = new Notification.Builder(mContext) 310 .setContentTitle("J Planning") 311 .setContentText("The Botcave") 312 .setWhen(System.currentTimeMillis()) 313 .setSmallIcon(R.drawable.stat_notify_calendar) 314 .setContentIntent(ToastService.getPendingIntent(mContext, "Clicked on calendar event")) 315 .setContentInfo("7PM") 316 .addAction(R.drawable.stat_notify_snooze, "+10 min", 317 ToastService.getPendingIntent(mContext, "snoozed 10 min")) 318 .addAction(R.drawable.stat_notify_snooze_longer, "+1 hour", 319 ToastService.getPendingIntent(mContext, "snoozed 1 hr")) 320 .addAction(R.drawable.stat_notify_email, "Email", 321 ToastService.getPendingIntent(mContext, 322 "Congratulations, you just destroyed someone's inbox zero")) 323 .build(); 324 L(" %s: create=%dms", summarize(n), SystemClock.currentThreadTimeMillis() - time); 325 mNotifications.add(n); 326 } 327 328 if (SHOW_BIG_PICTURE) { 329 BitmapDrawable d = 330 (BitmapDrawable) mContext.getResources().getDrawable(R.drawable.romainguy_rockaway); 331 final long time = SystemClock.currentThreadTimeMillis(); 332 final Notification n = new Notification.Builder(mContext) 333 .setContentTitle("Romain Guy") 334 .setContentText("I was lucky to find a Canon 5D Mk III at a local Bay Area " 335 + "store last week but I had not been able to try it in the field " 336 + "until tonight. After a few days of rain the sky finally cleared " 337 + "up. Rockaway Beach did not disappoint and I was finally able to " 338 + "see what my new camera feels like when shooting landscapes.") 339 .setSmallIcon(android.R.drawable.stat_notify_chat) 340 .setContentIntent( 341 ToastService.getPendingIntent(mContext, "Clicked picture")) 342 .setLargeIcon(getBitmap(mContext, R.drawable.romainguy_hed)) 343 .addAction(R.drawable.add, "Add to Gallery", 344 ToastService.getPendingIntent(mContext, "Added")) 345 .setStyle(new Notification.BigPictureStyle() 346 .bigPicture(d.getBitmap())) 347 .build(); 348 L(" %s: create=%dms", summarize(n), SystemClock.currentThreadTimeMillis() - time); 349 mNotifications.add(n); 350 } 351 352 if (SHOW_INBOX) { 353 final long time = SystemClock.currentThreadTimeMillis(); 354 final Notification n = new Notification.Builder(mContext) 355 .setContentTitle("New mail") 356 .setContentText("3 new messages") 357 .setSubText("example@gmail.com") 358 .setContentIntent(ToastService.getPendingIntent(mContext, "Clicked on Mail")) 359 .setSmallIcon(R.drawable.stat_notify_email) 360 .setStyle(new Notification.InboxStyle() 361 .setSummaryText("example@gmail.com") 362 .addLine(BOLD("Alice:").append(" hey there!")) 363 .addLine(BOLD("Bob:").append(" hi there!")) 364 .addLine(BOLD("Charlie:").append(" Iz IN UR EMAILZ!!")) 365 ).build(); 366 L(" %s: create=%dms", summarize(n), SystemClock.currentThreadTimeMillis() - time); 367 mNotifications.add(n); 368 } 369 370 if (SHOW_SOCIAL) { 371 final long time = SystemClock.currentThreadTimeMillis(); 372 final Notification n = new Notification.Builder(mContext) 373 .setContentTitle("Social Network") 374 .setContentText("You were mentioned in a post") 375 .setContentInfo("example@gmail.com") 376 .setContentIntent(ToastService.getPendingIntent(mContext, "Clicked on Social")) 377 .setSmallIcon(android.R.drawable.stat_notify_chat) 378 .setPriority(Notification.PRIORITY_LOW) 379 .build(); 380 L(" %s: create=%dms", summarize(n), SystemClock.currentThreadTimeMillis() - time); 381 mNotifications.add(n); 382 } 383 384 L("Posting notifications..."); 385 for (int i=0; i<mNotifications.size(); i++) { 386 final int count = 4; 387 for (int j=0; j<count; j++) { 388 long time = SystemClock.currentThreadTimeMillis(); 389 final Notification n = mNotifications.get(i); 390 noMa.notify(NOTIFICATION_ID + i, n); 391 time = SystemClock.currentThreadTimeMillis() - time; 392 L(" %s: notify=%dms (%d/%d)", summarize(n), time, 393 j + 1, count); 394 sleepIfYouCan(150); 395 } 396 } 397 398 sleepIfYouCan(1000); 399 400 L("Canceling notifications..."); 401 for (int i=0; i<mNotifications.size(); i++) { 402 final Notification n = mNotifications.get(i); 403 long time = SystemClock.currentThreadTimeMillis(); 404 noMa.cancel(NOTIFICATION_ID + i); 405 time = SystemClock.currentThreadTimeMillis() - time; 406 L(" %s: cancel=%dms", summarize(n), time); 407 } 408 409 sleepIfYouCan(500); 410 411 L("Parceling notifications..."); 412 // we want to be able to use this test on older OSes that do not have getBlobAshmemSize 413 Method getBlobAshmemSize = null; 414 try { 415 getBlobAshmemSize = Parcel.class.getMethod("getBlobAshmemSize"); 416 } catch (NoSuchMethodException ex) { 417 } 418 for (int i=0; i<mNotifications.size(); i++) { 419 Parcel p = Parcel.obtain(); 420 { 421 final Notification n = mNotifications.get(i); 422 long time = SystemClock.currentThreadTimeMillis(); 423 n.writeToParcel(p, 0); 424 time = SystemClock.currentThreadTimeMillis() - time; 425 L(" %s: write parcel=%dms size=%d ashmem=%s", 426 summarize(n), time, p.dataPosition(), 427 (getBlobAshmemSize != null) 428 ? getBlobAshmemSize.invoke(p) 429 : "???"); 430 p.setDataPosition(0); 431 } 432 433 long time = SystemClock.currentThreadTimeMillis(); 434 final Notification n2 = Notification.CREATOR.createFromParcel(p); 435 time = SystemClock.currentThreadTimeMillis() - time; 436 L(" %s: parcel read=%dms", summarize(n2), time); 437 438 time = SystemClock.currentThreadTimeMillis(); 439 noMa.notify(NOTIFICATION_ID + i, n2); 440 time = SystemClock.currentThreadTimeMillis() - time; 441 L(" %s: notify=%dms", summarize(n2), time); 442 } 443 444 sleepIfYouCan(500); 445 446 L("Canceling notifications..."); 447 for (int i=0; i<mNotifications.size(); i++) { 448 long time = SystemClock.currentThreadTimeMillis(); 449 final Notification n = mNotifications.get(i); 450 noMa.cancel(NOTIFICATION_ID + i); 451 time = SystemClock.currentThreadTimeMillis() - time; 452 L(" %s: cancel=%dms", summarize(n), time); 453 } 454 455 456 // if (SHOW_PROGRESS) { 457 // ProgressService.startProgressUpdater(this, uploadId, uploadWhen, 0); 458 // } 459 } 460 461 public static class FullScreenActivity extends Activity { 462 public static final String EXTRA_ID = "id"; 463 464 @Override onCreate(Bundle savedInstanceState)465 public void onCreate(Bundle savedInstanceState) { 466 super.onCreate(savedInstanceState); 467 setContentView(R.layout.full_screen); 468 final Intent intent = getIntent(); 469 if (intent != null && intent.hasExtra(EXTRA_ID)) { 470 final int id = intent.getIntExtra(EXTRA_ID, -1); 471 if (id >= 0) { 472 NotificationManager noMa = 473 (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); 474 noMa.cancel(NOTIFICATION_ID + id); 475 } 476 } 477 } 478 dismiss(View v)479 public void dismiss(View v) { 480 finish(); 481 } 482 getPendingIntent(Context context, int id)483 public static PendingIntent getPendingIntent(Context context, int id) { 484 Intent fullScreenIntent = new Intent(context, FullScreenActivity.class); 485 fullScreenIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 486 487 fullScreenIntent.putExtra(EXTRA_ID, id); 488 PendingIntent pi = PendingIntent.getActivity( 489 context, 22, fullScreenIntent, PendingIntent.FLAG_UPDATE_CURRENT); 490 return pi; 491 } 492 } 493 } 494 495