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 |