Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 package org.chromium.chrome.browser.notifications; | 5 package org.chromium.chrome.browser.notifications; |
| 6 | 6 |
| 7 import android.app.Notification; | 7 import android.app.Notification; |
| 8 import android.app.NotificationManager; | 8 import android.app.NotificationManager; |
| 9 import android.app.PendingIntent; | 9 import android.app.PendingIntent; |
| 10 import android.content.Context; | 10 import android.content.Context; |
| (...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 63 private static final int PENDING_INTENT_REQUEST_CODE = 0; | 63 private static final int PENDING_INTENT_REQUEST_CODE = 0; |
| 64 | 64 |
| 65 private static NotificationUIManager sInstance; | 65 private static NotificationUIManager sInstance; |
| 66 private static NotificationManagerProxy sNotificationManagerOverride; | 66 private static NotificationManagerProxy sNotificationManagerOverride; |
| 67 | 67 |
| 68 private final long mNativeNotificationManager; | 68 private final long mNativeNotificationManager; |
| 69 | 69 |
| 70 private final Context mAppContext; | 70 private final Context mAppContext; |
| 71 private final NotificationManagerProxy mNotificationManager; | 71 private final NotificationManagerProxy mNotificationManager; |
| 72 | 72 |
| 73 private RoundedIconGenerator mIconGenerator; | 73 @VisibleForTesting public RoundedIconGenerator mIconGenerator; |
| 74 private final int mLargeIconWidthPx; | |
| 75 private final int mLargeIconHeightPx; | |
| 76 private final float mDensity; | |
| 74 | 77 |
| 75 private long mLastNotificationClickMs = 0L; | 78 private long mLastNotificationClickMs = 0L; |
| 76 | 79 |
| 77 /** | 80 /** |
| 78 * Creates a new instance of the NotificationUIManager. | 81 * Creates a new instance of the NotificationUIManager. |
| 79 * | 82 * |
| 80 * @param nativeNotificationManager Instance of the NotificationUIManagerAnd roid class. | 83 * @param nativeNotificationManager Instance of the NotificationUIManagerAnd roid class. |
| 81 * @param context Application context for this instance of Chrome. | 84 * @param context Application context for this instance of Chrome. |
| 82 */ | 85 */ |
| 83 @CalledByNative | 86 @CalledByNative |
| 84 private static NotificationUIManager create(long nativeNotificationManager, Context context) { | 87 private static NotificationUIManager create(long nativeNotificationManager, Context context) { |
| 85 if (sInstance != null) { | 88 if (sInstance != null) { |
| 86 throw new IllegalStateException("There must only be a single Notific ationUIManager."); | 89 throw new IllegalStateException("There must only be a single Notific ationUIManager."); |
| 87 } | 90 } |
| 88 | 91 |
| 89 sInstance = new NotificationUIManager(nativeNotificationManager, context ); | 92 sInstance = new NotificationUIManager(nativeNotificationManager, context ); |
| 90 return sInstance; | 93 return sInstance; |
| 91 } | 94 } |
| 92 | 95 |
| 93 /** | 96 /** |
| 97 * Returns the current instance of the NotificationUIManager. Should only be used by tests. | |
|
Miguel Garcia
2015/09/28 17:47:12
Doesn't the @VisibleForTesting annotation already
Peter Beverloo
2015/09/28 18:03:03
Done.
| |
| 98 * | |
| 99 * @return The instance of the NotificationUIManager, if any. | |
| 100 */ | |
| 101 @Nullable | |
| 102 @VisibleForTesting | |
| 103 public static NotificationUIManager getInstanceForTests() { | |
| 104 return sInstance; | |
| 105 } | |
| 106 | |
| 107 /** | |
| 94 * Overrides the notification manager which is to be used for displaying Not ifications on the | 108 * Overrides the notification manager which is to be used for displaying Not ifications on the |
| 95 * Android framework. Should only be used for testing. Tests are expected to clean up after | 109 * Android framework. Should only be used for testing. Tests are expected to clean up after |
| 96 * themselves by setting this to NULL again. | 110 * themselves by setting this to NULL again. |
| 97 * | 111 * |
| 98 * @param proxy The notification manager instance to use instead of the syst em's. | 112 * @param proxy The notification manager instance to use instead of the syst em's. |
| 99 */ | 113 */ |
| 100 @VisibleForTesting | 114 @VisibleForTesting |
| 101 public static void overrideNotificationManagerForTesting( | 115 public static void overrideNotificationManagerForTesting( |
| 102 NotificationManagerProxy notificationManager) { | 116 NotificationManagerProxy notificationManager) { |
| 103 sNotificationManagerOverride = notificationManager; | 117 sNotificationManagerOverride = notificationManager; |
| 104 } | 118 } |
| 105 | 119 |
| 106 private NotificationUIManager(long nativeNotificationManager, Context contex t) { | 120 private NotificationUIManager(long nativeNotificationManager, Context contex t) { |
| 107 mNativeNotificationManager = nativeNotificationManager; | 121 mNativeNotificationManager = nativeNotificationManager; |
| 108 mAppContext = context.getApplicationContext(); | 122 mAppContext = context.getApplicationContext(); |
| 109 | 123 |
| 110 if (sNotificationManagerOverride != null) { | 124 if (sNotificationManagerOverride != null) { |
| 111 mNotificationManager = sNotificationManagerOverride; | 125 mNotificationManager = sNotificationManagerOverride; |
| 112 } else { | 126 } else { |
| 113 mNotificationManager = new NotificationManagerProxyImpl( | 127 mNotificationManager = new NotificationManagerProxyImpl( |
| 114 (NotificationManager) context.getSystemService(Context.NOTIF ICATION_SERVICE)); | 128 (NotificationManager) context.getSystemService(Context.NOTIF ICATION_SERVICE)); |
| 115 } | 129 } |
| 130 | |
| 131 Resources resources = mAppContext.getResources(); | |
| 132 | |
|
Miguel Garcia
2015/09/28 17:47:13
nit can you initialize density after line 137 so i
Peter Beverloo
2015/09/28 18:03:03
Done.
| |
| 133 mDensity = resources.getDisplayMetrics().density; | |
| 134 mLargeIconWidthPx = | |
|
Miguel Garcia
2015/09/28 17:47:13
I assume neither of this can ever return 0? Otherw
Peter Beverloo
2015/09/28 18:03:03
Stack Overflow would have exploded already if that
| |
| 135 resources.getDimensionPixelSize(android.R.dimen.notification_large_i con_width); | |
| 136 mLargeIconHeightPx = | |
| 137 resources.getDimensionPixelSize(android.R.dimen.notification_large_i con_height); | |
| 116 } | 138 } |
| 117 | 139 |
| 118 /** | 140 /** |
| 119 * Marks the current instance as being freed, allowing for a new Notificatio nUIManager | 141 * Marks the current instance as being freed, allowing for a new Notificatio nUIManager |
| 120 * object to be initialized. | 142 * object to be initialized. |
| 121 */ | 143 */ |
| 122 @CalledByNative | 144 @CalledByNative |
| 123 private void destroy() { | 145 private void destroy() { |
| 124 assert sInstance == this; | 146 assert sInstance == this; |
| 125 sInstance = null; | 147 sInstance = null; |
| (...skipping 239 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 365 * | 387 * |
| 366 * @param persistentNotificationId The persistent id of the notification. | 388 * @param persistentNotificationId The persistent id of the notification. |
| 367 * @param origin Full text of the origin, including the protocol, owning thi s notification. | 389 * @param origin Full text of the origin, including the protocol, owning thi s notification. |
| 368 * @param tag A string identifier for this notification. If the tag is not e mpty, the new | 390 * @param tag A string identifier for this notification. If the tag is not e mpty, the new |
| 369 * notification will replace the previous notification with the s ame tag and origin, | 391 * notification will replace the previous notification with the s ame tag and origin, |
| 370 * if present. If no matching previous notification is present, t he new one will just | 392 * if present. If no matching previous notification is present, t he new one will just |
| 371 * be added. | 393 * be added. |
| 372 * @param title Title to be displayed in the notification. | 394 * @param title Title to be displayed in the notification. |
| 373 * @param body Message to be displayed in the notification. Will be trimmed to one line of | 395 * @param body Message to be displayed in the notification. Will be trimmed to one line of |
| 374 * text by the Android notification system. | 396 * text by the Android notification system. |
| 375 * @param icon Icon to be displayed in the notification. When this isn't a v alid Bitmap, a | 397 * @param icon Icon to be displayed in the notification. Valid Bitmap icons will be scaled to |
| 376 * default icon will be generated instead. | 398 * the platforms, whereas a default icon will be generated for i nvalid Bitmaps. |
| 377 * @param vibrationPattern Vibration pattern following the Web Vibration syn tax. | 399 * @param vibrationPattern Vibration pattern following the Web Vibration syn tax. |
| 378 * @param silent Whether the default sound, vibration and lights should be s uppressed. | 400 * @param silent Whether the default sound, vibration and lights should be s uppressed. |
| 379 * @param actionTitles Titles of actions to display alongside the notificati on. | 401 * @param actionTitles Titles of actions to display alongside the notificati on. |
| 380 * @see https://developer.android.com/reference/android/app/Notification.htm l | 402 * @see https://developer.android.com/reference/android/app/Notification.htm l |
| 381 */ | 403 */ |
| 382 @CalledByNative | 404 @CalledByNative |
| 383 private void displayNotification(long persistentNotificationId, String origi n, String tag, | 405 private void displayNotification(long persistentNotificationId, String origi n, String tag, |
| 384 String title, String body, Bitmap icon, int[] vibrationPattern, bool ean silent, | 406 String title, String body, Bitmap icon, int[] vibrationPattern, bool ean silent, |
| 385 String[] actionTitles) { | 407 String[] actionTitles) { |
| 386 if (icon == null || icon.getWidth() == 0) { | |
| 387 icon = getIconGenerator().generateIconForUrl(origin, true); | |
| 388 } | |
| 389 | |
| 390 Resources res = mAppContext.getResources(); | 408 Resources res = mAppContext.getResources(); |
| 391 | 409 |
| 392 // Set up a pending intent for going to the settings screen for |origin| . | 410 // Set up a pending intent for going to the settings screen for |origin| . |
| 393 Intent settingsIntent = PreferencesLauncher.createIntentForSettingsPage( | 411 Intent settingsIntent = PreferencesLauncher.createIntentForSettingsPage( |
| 394 mAppContext, SingleWebsitePreferences.class.getName()); | 412 mAppContext, SingleWebsitePreferences.class.getName()); |
| 395 settingsIntent.setData(makeIntentData(persistentNotificationId, origin, | 413 settingsIntent.setData(makeIntentData(persistentNotificationId, origin, |
| 396 -1 /* actionIndex */)); | 414 -1 /* actionIndex */)); |
| 397 settingsIntent.putExtra(Preferences.EXTRA_SHOW_FRAGMENT_ARGUMENTS, | 415 settingsIntent.putExtra(Preferences.EXTRA_SHOW_FRAGMENT_ARGUMENTS, |
| 398 SingleWebsitePreferences.createFragmentArgsForSite(origin)); | 416 SingleWebsitePreferences.createFragmentArgsForSite(origin)); |
| 399 | 417 |
| 400 PendingIntent pendingSettingsIntent = PendingIntent.getActivity(mAppCont ext, | 418 PendingIntent pendingSettingsIntent = PendingIntent.getActivity(mAppCont ext, |
| 401 PENDING_INTENT_REQUEST_CODE, settingsIntent, PendingIntent.FLAG_ UPDATE_CURRENT); | 419 PENDING_INTENT_REQUEST_CODE, settingsIntent, PendingIntent.FLAG_ UPDATE_CURRENT); |
| 402 | 420 |
| 403 NotificationCompat.Builder notificationBuilder = new NotificationCompat. Builder(mAppContext) | 421 NotificationCompat.Builder notificationBuilder = new NotificationCompat. Builder(mAppContext) |
| 404 .setContentTitle(title) | 422 .setContentTitle(title) |
| 405 .setContentText(body) | 423 .setContentText(body) |
| 406 .setStyle(new NotificationCompat.BigTextStyle().bigText(body)) | 424 .setStyle(new NotificationCompat.BigTextStyle().bigText(body)) |
| 407 .setLargeIcon(icon) | 425 .setLargeIcon(ensureNormalizedIcon(icon, origin)) |
| 408 .setSmallIcon(R.drawable.ic_chrome) | 426 .setSmallIcon(R.drawable.ic_chrome) |
| 409 .setContentIntent(makePendingIntent( | 427 .setContentIntent(makePendingIntent( |
| 410 NotificationConstants.ACTION_CLICK_NOTIFICATION, | 428 NotificationConstants.ACTION_CLICK_NOTIFICATION, |
| 411 persistentNotificationId, origin, tag, -1 /* actionIndex */)) | 429 persistentNotificationId, origin, tag, -1 /* actionIndex */)) |
| 412 .setDeleteIntent(makePendingIntent( | 430 .setDeleteIntent(makePendingIntent( |
| 413 NotificationConstants.ACTION_CLOSE_NOTIFICATION, | 431 NotificationConstants.ACTION_CLOSE_NOTIFICATION, |
| 414 persistentNotificationId, origin, tag, -1 /* actionIndex */)) | 432 persistentNotificationId, origin, tag, -1 /* actionIndex */)) |
| 415 .setTicker(createTickerText(title, body)) | 433 .setTicker(createTickerText(title, body)) |
| 416 .setSubText(origin); | 434 .setSubText(origin); |
| 417 | 435 |
| (...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 452 spannableStringBuilder.append(body); | 470 spannableStringBuilder.append(body); |
| 453 | 471 |
| 454 // Mark the title of the notification as being bold. | 472 // Mark the title of the notification as being bold. |
| 455 spannableStringBuilder.setSpan(new StyleSpan(android.graphics.Typeface.B OLD), | 473 spannableStringBuilder.setSpan(new StyleSpan(android.graphics.Typeface.B OLD), |
| 456 0, title.length(), Spannable.SPAN_INCLUSIVE_INCLUSIVE); | 474 0, title.length(), Spannable.SPAN_INCLUSIVE_INCLUSIVE); |
| 457 | 475 |
| 458 return spannableStringBuilder; | 476 return spannableStringBuilder; |
| 459 } | 477 } |
| 460 | 478 |
| 461 /** | 479 /** |
| 462 * Ensures the existance of an icon generator, which is created lazily. | 480 * Ensures the availability of an icon for the notification. |
| 463 * | 481 * |
| 464 * @return The icon generator which can be used. | 482 * If |icon| is a valid, non-empty Bitmap, the bitmap will be scaled to be o f an appropriate |
| 483 * size for the current Android device. Otherwise, a default icon will be cr eated based on the | |
| 484 * origin the notification is being displayed for. | |
| 485 * | |
| 486 * @param icon The developer-provided icon they intend to use for the notifi cation. | |
| 487 * @param origin The origin the notification is being displayed for. | |
| 488 * @return An appropriately sized icon to use for the notification. | |
| 465 */ | 489 */ |
| 466 private RoundedIconGenerator getIconGenerator() { | 490 @VisibleForTesting |
| 467 if (mIconGenerator == null) { | 491 public Bitmap ensureNormalizedIcon(Bitmap icon, String origin) { |
| 468 mIconGenerator = createRoundedIconGenerator(mAppContext); | 492 if (icon == null || icon.getWidth() == 0) { |
| 493 if (mIconGenerator == null) { | |
| 494 int cornerRadiusPx = Math.min(mLargeIconWidthPx, mLargeIconHeigh tPx) / 2; | |
| 495 mIconGenerator = | |
| 496 new RoundedIconGenerator(mLargeIconWidthPx, mLargeIconHe ightPx, | |
| 497 cornerRadiusPx, | |
| 498 NOTIFICATION_ICON_BG_COLOR, | |
| 499 NOTIFICATION_TEXT_SIZE_DP * mDe nsity); | |
| 500 } | |
| 501 | |
| 502 return mIconGenerator.generateIconForUrl(origin, true); | |
| 469 } | 503 } |
| 470 | 504 |
| 471 return mIconGenerator; | 505 if (icon.getWidth() > mLargeIconWidthPx || icon.getHeight() > mLargeIcon HeightPx) { |
| 506 return icon.createScaledBitmap(icon, mLargeIconWidthPx, mLargeIconHe ightPx, false); | |
|
Miguel Garcia
2015/09/28 17:47:12
can you add /* not filtered */ right after the las
Peter Beverloo
2015/09/28 18:03:03
Done.
| |
| 507 } | |
| 508 | |
| 509 return icon; | |
| 472 } | 510 } |
| 473 | 511 |
| 474 /** | 512 /** |
| 475 * Creates the rounded icon generator to use for notifications based on the dimensions | |
| 476 * and resolution of the device we're running on. | |
| 477 * | |
| 478 * @param appContext The application context to retrieve resources from. | |
| 479 * @return The newly created rounded icon generator. | |
| 480 */ | |
| 481 @VisibleForTesting | |
| 482 public static RoundedIconGenerator createRoundedIconGenerator(Context appCon text) { | |
| 483 Resources res = appContext.getResources(); | |
| 484 float density = res.getDisplayMetrics().density; | |
| 485 | |
| 486 int widthPx = res.getDimensionPixelSize(android.R.dimen.notification_lar ge_icon_width); | |
| 487 int heightPx = | |
| 488 res.getDimensionPixelSize(android.R.dimen.notification_large_ico n_height); | |
| 489 | |
| 490 return new RoundedIconGenerator( | |
| 491 widthPx, | |
| 492 heightPx, | |
| 493 Math.min(widthPx, heightPx) / 2, | |
| 494 NOTIFICATION_ICON_BG_COLOR, | |
| 495 NOTIFICATION_TEXT_SIZE_DP * density); | |
| 496 } | |
| 497 | |
| 498 /** | |
| 499 * Returns whether a notification has been clicked in the last 5 seconds. | 513 * Returns whether a notification has been clicked in the last 5 seconds. |
| 500 * Used for Startup.BringToForegroundReason UMA histogram. | 514 * Used for Startup.BringToForegroundReason UMA histogram. |
| 501 */ | 515 */ |
| 502 public static boolean wasNotificationRecentlyClicked() { | 516 public static boolean wasNotificationRecentlyClicked() { |
| 503 if (sInstance == null) return false; | 517 if (sInstance == null) return false; |
| 504 long now = System.currentTimeMillis(); | 518 long now = System.currentTimeMillis(); |
| 505 return now - sInstance.mLastNotificationClickMs < 5 * 1000; | 519 return now - sInstance.mLastNotificationClickMs < 5 * 1000; |
| 506 } | 520 } |
| 507 | 521 |
| 508 /** | 522 /** |
| (...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 550 origin, tag); | 564 origin, tag); |
| 551 } | 565 } |
| 552 | 566 |
| 553 private static native void nativeInitializeNotificationUIManager(); | 567 private static native void nativeInitializeNotificationUIManager(); |
| 554 | 568 |
| 555 private native boolean nativeOnNotificationClicked(long nativeNotificationUI ManagerAndroid, | 569 private native boolean nativeOnNotificationClicked(long nativeNotificationUI ManagerAndroid, |
| 556 long persistentNotificationId, String origin, String tag, int action Index); | 570 long persistentNotificationId, String origin, String tag, int action Index); |
| 557 private native boolean nativeOnNotificationClosed(long nativeNotificationUIM anagerAndroid, | 571 private native boolean nativeOnNotificationClosed(long nativeNotificationUIM anagerAndroid, |
| 558 long persistentNotificationId, String origin, String tag); | 572 long persistentNotificationId, String origin, String tag); |
| 559 } | 573 } |
| OLD | NEW |