| OLD | NEW |
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 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.download; | 5 package org.chromium.chrome.browser.download; |
| 6 | 6 |
| 7 import android.app.ActivityManager; | 7 import android.app.ActivityManager; |
| 8 import android.app.DownloadManager; | 8 import android.app.DownloadManager; |
| 9 import android.app.Notification; | 9 import android.app.Notification; |
| 10 import android.app.NotificationManager; | 10 import android.app.NotificationManager; |
| 11 import android.app.PendingIntent; | 11 import android.app.PendingIntent; |
| 12 import android.app.Service; | 12 import android.app.Service; |
| 13 import android.content.ComponentName; | 13 import android.content.ComponentName; |
| 14 import android.content.Context; | 14 import android.content.Context; |
| 15 import android.content.Intent; | 15 import android.content.Intent; |
| 16 import android.content.SharedPreferences; | 16 import android.content.SharedPreferences; |
| 17 import android.content.res.Resources; | 17 import android.content.res.Resources; |
| 18 import android.graphics.Bitmap; | 18 import android.graphics.Bitmap; |
| 19 import android.graphics.BitmapFactory; | 19 import android.graphics.BitmapFactory; |
| 20 import android.graphics.Canvas; | 20 import android.graphics.Canvas; |
| 21 import android.graphics.Paint; | 21 import android.graphics.Paint; |
| 22 import android.graphics.Rect; | 22 import android.graphics.Rect; |
| 23 import android.graphics.drawable.shapes.OvalShape; | 23 import android.graphics.drawable.shapes.OvalShape; |
| 24 import android.os.Binder; | 24 import android.os.Binder; |
| 25 import android.os.Build; | 25 import android.os.Build; |
| 26 import android.os.IBinder; | 26 import android.os.IBinder; |
| 27 import android.support.v4.app.NotificationCompat; | |
| 28 import android.text.TextUtils; | 27 import android.text.TextUtils; |
| 29 | 28 |
| 30 import org.chromium.base.ApiCompatibilityUtils; | 29 import org.chromium.base.ApiCompatibilityUtils; |
| 31 import org.chromium.base.ContextUtils; | 30 import org.chromium.base.ContextUtils; |
| 32 import org.chromium.base.Log; | 31 import org.chromium.base.Log; |
| 33 import org.chromium.base.VisibleForTesting; | 32 import org.chromium.base.VisibleForTesting; |
| 34 import org.chromium.base.library_loader.ProcessInitException; | 33 import org.chromium.base.library_loader.ProcessInitException; |
| 35 import org.chromium.chrome.R; | 34 import org.chromium.chrome.R; |
| 36 import org.chromium.chrome.browser.ChromeApplication; | 35 import org.chromium.chrome.browser.ChromeApplication; |
| 37 import org.chromium.chrome.browser.init.BrowserParts; | 36 import org.chromium.chrome.browser.init.BrowserParts; |
| 38 import org.chromium.chrome.browser.init.ChromeBrowserInitializer; | 37 import org.chromium.chrome.browser.init.ChromeBrowserInitializer; |
| 39 import org.chromium.chrome.browser.init.EmptyBrowserParts; | 38 import org.chromium.chrome.browser.init.EmptyBrowserParts; |
| 39 import org.chromium.chrome.browser.notifications.ChromeNotificationBuilder; |
| 40 import org.chromium.chrome.browser.notifications.NotificationConstants; | 40 import org.chromium.chrome.browser.notifications.NotificationConstants; |
| 41 import org.chromium.chrome.browser.offlinepages.downloads.OfflinePageDownloadBri
dge; | 41 import org.chromium.chrome.browser.offlinepages.downloads.OfflinePageDownloadBri
dge; |
| 42 import org.chromium.chrome.browser.util.IntentUtils; | 42 import org.chromium.chrome.browser.util.IntentUtils; |
| 43 | 43 |
| 44 import java.util.ArrayList; | 44 import java.util.ArrayList; |
| 45 import java.util.List; | 45 import java.util.List; |
| 46 import java.util.concurrent.TimeUnit; | 46 import java.util.concurrent.TimeUnit; |
| 47 | 47 |
| 48 | 48 |
| 49 /** | 49 /** |
| (...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 120 // This funcion is called when Chrome is swiped away from the recent app
s | 120 // This funcion is called when Chrome is swiped away from the recent app
s |
| 121 // drawer. So it doesn't catch all scenarios that chrome can get killed. | 121 // drawer. So it doesn't catch all scenarios that chrome can get killed. |
| 122 // This will only help Android 4.4.2. | 122 // This will only help Android 4.4.2. |
| 123 onBrowserKilled(); | 123 onBrowserKilled(); |
| 124 mStopPostingProgressNotifications = true; | 124 mStopPostingProgressNotifications = true; |
| 125 } | 125 } |
| 126 | 126 |
| 127 @Override | 127 @Override |
| 128 public void onCreate() { | 128 public void onCreate() { |
| 129 mStopPostingProgressNotifications = false; | 129 mStopPostingProgressNotifications = false; |
| 130 mContext = getApplicationContext(); | 130 mContext = ContextUtils.getApplicationContext(); |
| 131 mNotificationManager = (NotificationManager) mContext.getSystemService( | 131 mNotificationManager = (NotificationManager) mContext.getSystemService( |
| 132 Context.NOTIFICATION_SERVICE); | 132 Context.NOTIFICATION_SERVICE); |
| 133 mSharedPrefs = ContextUtils.getAppSharedPreferences(); | 133 mSharedPrefs = ContextUtils.getAppSharedPreferences(); |
| 134 mNumAutoResumptionAttemptLeft = mSharedPrefs.getInt(KEY_AUTO_RESUMPTION_
ATTEMPT_LEFT, | 134 mNumAutoResumptionAttemptLeft = mSharedPrefs.getInt(KEY_AUTO_RESUMPTION_
ATTEMPT_LEFT, |
| 135 MAX_RESUMPTION_ATTEMPT_LEFT); | 135 MAX_RESUMPTION_ATTEMPT_LEFT); |
| 136 mDownloadSharedPreferenceHelper = DownloadSharedPreferenceHelper.getInst
ance(); | 136 mDownloadSharedPreferenceHelper = DownloadSharedPreferenceHelper.getInst
ance(); |
| 137 // Because this service is a started service and returns START_STICKY in | 137 // Because this service is a started service and returns START_STICKY in |
| 138 // onStartCommand(), it will be restarted as soon as resources are avail
able | 138 // onStartCommand(), it will be restarted as soon as resources are avail
able |
| 139 // after it is killed. As a result, onCreate() may be called after Chrom
e | 139 // after it is killed. As a result, onCreate() may be called after Chrom
e |
| 140 // gets killed and before user restarts chrome. In that case, | 140 // gets killed and before user restarts chrome. In that case, |
| (...skipping 150 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 291 } else if (indeterminate) { | 291 } else if (indeterminate) { |
| 292 contentText = DownloadUtils.getStringForBytes( | 292 contentText = DownloadUtils.getStringForBytes( |
| 293 mContext, DownloadUtils.BYTES_DOWNLOADED_STRINGS, bytesRecei
ved); | 293 mContext, DownloadUtils.BYTES_DOWNLOADED_STRINGS, bytesRecei
ved); |
| 294 } else { | 294 } else { |
| 295 contentText = timeRemainingInMillis < 0 | 295 contentText = timeRemainingInMillis < 0 |
| 296 ? mContext.getResources().getString(R.string.download_starte
d) | 296 ? mContext.getResources().getString(R.string.download_starte
d) |
| 297 : formatRemainingTime(mContext, timeRemainingInMillis); | 297 : formatRemainingTime(mContext, timeRemainingInMillis); |
| 298 } | 298 } |
| 299 int resId = isDownloadPending ? R.drawable.ic_download_pending | 299 int resId = isDownloadPending ? R.drawable.ic_download_pending |
| 300 : android.R.drawable.stat_sys_download; | 300 : android.R.drawable.stat_sys_download; |
| 301 NotificationCompat.Builder builder = buildNotification(resId, fileName,
contentText); | 301 ChromeNotificationBuilder builder = buildNotification(resId, fileName, c
ontentText); |
| 302 builder.setOngoing(true); | 302 builder.setOngoing(true); |
| 303 builder.setPriority(Notification.PRIORITY_HIGH); | 303 builder.setPriority(Notification.PRIORITY_HIGH); |
| 304 | 304 |
| 305 // Avoid animations while the download isn't progressing. | 305 // Avoid animations while the download isn't progressing. |
| 306 if (!isDownloadPending) { | 306 if (!isDownloadPending) { |
| 307 builder.setProgress(100, percentage, indeterminate); | 307 builder.setProgress(100, percentage, indeterminate); |
| 308 } | 308 } |
| 309 | 309 |
| 310 if (!indeterminate && !isOfflinePage) { | 310 if (!indeterminate && !isOfflinePage) { |
| 311 String percentText = DownloadUtils.getPercentageString(percentage); | 311 String percentText = DownloadUtils.getPercentageString(percentage); |
| (...skipping 82 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 394 // If download is interrupted due to network disconnection, show downloa
d pending state. | 394 // If download is interrupted due to network disconnection, show downloa
d pending state. |
| 395 if (isAutoResumable) { | 395 if (isAutoResumable) { |
| 396 notifyDownloadPending(entry.downloadGuid, entry.fileName, entry.isOf
fTheRecord, | 396 notifyDownloadPending(entry.downloadGuid, entry.fileName, entry.isOf
fTheRecord, |
| 397 entry.canDownloadWhileMetered, entry.isOfflinePage()); | 397 entry.canDownloadWhileMetered, entry.isOfflinePage()); |
| 398 mDownloadsInProgress.remove(downloadGuid); | 398 mDownloadsInProgress.remove(downloadGuid); |
| 399 return; | 399 return; |
| 400 } | 400 } |
| 401 | 401 |
| 402 String contentText = mContext.getResources().getString( | 402 String contentText = mContext.getResources().getString( |
| 403 R.string.download_notification_paused); | 403 R.string.download_notification_paused); |
| 404 NotificationCompat.Builder builder = buildNotification( | 404 ChromeNotificationBuilder builder = |
| 405 R.drawable.ic_download_pause, entry.fileName, contentText); | 405 buildNotification(R.drawable.ic_download_pause, entry.fileName,
contentText); |
| 406 | 406 |
| 407 // Clicking on an in-progress download sends the user to see all their d
ownloads. | 407 // Clicking on an in-progress download sends the user to see all their d
ownloads. |
| 408 Intent downloadHomeIntent = buildActionIntent( | 408 Intent downloadHomeIntent = buildActionIntent( |
| 409 mContext, DownloadManager.ACTION_NOTIFICATION_CLICKED, null, fal
se, false); | 409 mContext, DownloadManager.ACTION_NOTIFICATION_CLICKED, null, fal
se, false); |
| 410 builder.setContentIntent(PendingIntent.getBroadcast(mContext, entry.noti
ficationId, | 410 builder.setContentIntent(PendingIntent.getBroadcast(mContext, entry.noti
ficationId, |
| 411 downloadHomeIntent, PendingIntent.FLAG_UPDATE_CURRENT)); | 411 downloadHomeIntent, PendingIntent.FLAG_UPDATE_CURRENT)); |
| 412 builder.setAutoCancel(false); | 412 builder.setAutoCancel(false); |
| 413 | 413 |
| 414 Intent resumeIntent = buildActionIntent(mContext, ACTION_DOWNLOAD_RESUME
, | 414 Intent resumeIntent = buildActionIntent(mContext, ACTION_DOWNLOAD_RESUME
, |
| 415 entry.downloadGuid, entry.isOffTheRecord, entry.isOfflinePage())
; | 415 entry.downloadGuid, entry.isOffTheRecord, entry.isOfflinePage())
; |
| (...skipping 30 matching lines...) Expand all Loading... |
| 446 * @param isOfflinePage Whether the download is for offline page. | 446 * @param isOfflinePage Whether the download is for offline page. |
| 447 * @param isSupportedMimeType Whether the MIME type can be viewed inside bro
wser. | 447 * @param isSupportedMimeType Whether the MIME type can be viewed inside bro
wser. |
| 448 * @return ID of the successful download notification. Used for removing the
notification when | 448 * @return ID of the successful download notification. Used for removing the
notification when |
| 449 * user click on the snackbar. | 449 * user click on the snackbar. |
| 450 */ | 450 */ |
| 451 @VisibleForTesting | 451 @VisibleForTesting |
| 452 public int notifyDownloadSuccessful( | 452 public int notifyDownloadSuccessful( |
| 453 String downloadGuid, String filePath, String fileName, long systemDo
wnloadId, | 453 String downloadGuid, String filePath, String fileName, long systemDo
wnloadId, |
| 454 boolean isOfflinePage, boolean isSupportedMimeType) { | 454 boolean isOfflinePage, boolean isSupportedMimeType) { |
| 455 int notificationId = getNotificationId(downloadGuid); | 455 int notificationId = getNotificationId(downloadGuid); |
| 456 NotificationCompat.Builder builder = buildNotification( | 456 ChromeNotificationBuilder builder = buildNotification(R.drawable.offline
_pin, fileName, |
| 457 R.drawable.offline_pin, fileName, | |
| 458 mContext.getResources().getString(R.string.download_notification
_completed)); | 457 mContext.getResources().getString(R.string.download_notification
_completed)); |
| 459 ComponentName component = new ComponentName( | 458 ComponentName component = new ComponentName( |
| 460 mContext.getPackageName(), DownloadBroadcastReceiver.class.getNa
me()); | 459 mContext.getPackageName(), DownloadBroadcastReceiver.class.getNa
me()); |
| 461 Intent intent; | 460 Intent intent; |
| 462 if (isOfflinePage) { | 461 if (isOfflinePage) { |
| 463 intent = buildActionIntent(mContext, ACTION_DOWNLOAD_OPEN, downloadG
uid, false, true); | 462 intent = buildActionIntent(mContext, ACTION_DOWNLOAD_OPEN, downloadG
uid, false, true); |
| 464 } else { | 463 } else { |
| 465 intent = new Intent(DownloadManager.ACTION_NOTIFICATION_CLICKED); | 464 intent = new Intent(DownloadManager.ACTION_NOTIFICATION_CLICKED); |
| 466 long[] idArray = {systemDownloadId}; | 465 long[] idArray = {systemDownloadId}; |
| 467 intent.putExtra(DownloadManager.EXTRA_NOTIFICATION_CLICK_DOWNLOAD_ID
S, idArray); | 466 intent.putExtra(DownloadManager.EXTRA_NOTIFICATION_CLICK_DOWNLOAD_ID
S, idArray); |
| (...skipping 25 matching lines...) Expand all Loading... |
| 493 // If the download is not in history db, fileName could be empty. Get it
from | 492 // If the download is not in history db, fileName could be empty. Get it
from |
| 494 // SharedPreferences. | 493 // SharedPreferences. |
| 495 if (TextUtils.isEmpty(fileName)) { | 494 if (TextUtils.isEmpty(fileName)) { |
| 496 DownloadSharedPreferenceEntry entry = | 495 DownloadSharedPreferenceEntry entry = |
| 497 mDownloadSharedPreferenceHelper.getDownloadSharedPreferenceE
ntry(downloadGuid); | 496 mDownloadSharedPreferenceHelper.getDownloadSharedPreferenceE
ntry(downloadGuid); |
| 498 if (entry == null) return; | 497 if (entry == null) return; |
| 499 fileName = entry.fileName; | 498 fileName = entry.fileName; |
| 500 } | 499 } |
| 501 | 500 |
| 502 int notificationId = getNotificationId(downloadGuid); | 501 int notificationId = getNotificationId(downloadGuid); |
| 503 NotificationCompat.Builder builder = buildNotification( | 502 ChromeNotificationBuilder builder = |
| 504 android.R.drawable.stat_sys_download_done, fileName, | 503 buildNotification(android.R.drawable.stat_sys_download_done, fil
eName, |
| 505 mContext.getResources().getString(R.string.download_notification
_failed)); | 504 mContext.getResources().getString(R.string.download_noti
fication_failed)); |
| 506 updateNotification(notificationId, builder.build()); | 505 updateNotification(notificationId, builder.build()); |
| 507 mDownloadSharedPreferenceHelper.removeSharedPreferenceEntry(downloadGuid
); | 506 mDownloadSharedPreferenceHelper.removeSharedPreferenceEntry(downloadGuid
); |
| 508 mDownloadsInProgress.remove(downloadGuid); | 507 mDownloadsInProgress.remove(downloadGuid); |
| 509 } | 508 } |
| 510 | 509 |
| 511 /** | 510 /** |
| 512 * Called to pause all the download notifications. | 511 * Called to pause all the download notifications. |
| 513 */ | 512 */ |
| 514 @VisibleForTesting | 513 @VisibleForTesting |
| 515 void pauseAllDownloads() { | 514 void pauseAllDownloads() { |
| (...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 563 return intent; | 562 return intent; |
| 564 } | 563 } |
| 565 | 564 |
| 566 /** | 565 /** |
| 567 * Builds a notification to be displayed. | 566 * Builds a notification to be displayed. |
| 568 * @param iconId Id of the notification icon. | 567 * @param iconId Id of the notification icon. |
| 569 * @param title Title of the notification. | 568 * @param title Title of the notification. |
| 570 * @param contentText Notification content text to be displayed. | 569 * @param contentText Notification content text to be displayed. |
| 571 * @return notification builder that builds the notification to be displayed | 570 * @return notification builder that builds the notification to be displayed |
| 572 */ | 571 */ |
| 573 private NotificationCompat.Builder buildNotification( | 572 private ChromeNotificationBuilder buildNotification( |
| 574 int iconId, String title, String contentText) { | 573 int iconId, String title, String contentText) { |
| 575 NotificationCompat.Builder builder = new NotificationCompat.Builder(mCon
text) | 574 ChromeNotificationBuilder builder = |
| 576 .setContentTitle(DownloadUtils.getAbbreviatedFileName(title, MAX
_FILE_NAME_LENGTH)) | 575 ((ChromeApplication) mContext) |
| 577 .setSmallIcon(iconId) | 576 .createChromeNotificationBuilder(true /* preferCompat */
, |
| 578 .setLocalOnly(true) | 577 NotificationConstants.CATEGORY_ID_BROWSER, |
| 579 .setAutoCancel(true) | 578 mContext.getString(R.string.notification_categor
y_browser), |
| 580 .setContentText(contentText) | 579 NotificationConstants.CATEGORY_GROUP_ID_GENERAL, |
| 581 .setGroup(NotificationConstants.GROUP_DOWNLOADS); | 580 mContext.getString(R.string.notification_categor
y_group_general)) |
| 581 .setContentTitle( |
| 582 DownloadUtils.getAbbreviatedFileName(title, MAX_
FILE_NAME_LENGTH)) |
| 583 .setSmallIcon(iconId) |
| 584 .setLocalOnly(true) |
| 585 .setAutoCancel(true) |
| 586 .setContentText(contentText) |
| 587 .setGroup(NotificationConstants.GROUP_DOWNLOADS); |
| 582 return builder; | 588 return builder; |
| 583 } | 589 } |
| 584 | 590 |
| 585 private Bitmap getLargeNotificationIcon(Bitmap bitmap) { | 591 private Bitmap getLargeNotificationIcon(Bitmap bitmap) { |
| 586 Resources resources = mContext.getResources(); | 592 Resources resources = mContext.getResources(); |
| 587 int height = (int) resources.getDimension(android.R.dimen.notification_l
arge_icon_height); | 593 int height = (int) resources.getDimension(android.R.dimen.notification_l
arge_icon_height); |
| 588 int width = (int) resources.getDimension(android.R.dimen.notification_la
rge_icon_width); | 594 int width = (int) resources.getDimension(android.R.dimen.notification_la
rge_icon_width); |
| 589 final OvalShape circle = new OvalShape(); | 595 final OvalShape circle = new OvalShape(); |
| 590 circle.resize(width, height); | 596 circle.resize(width, height); |
| 591 final Paint paint = new Paint(); | 597 final Paint paint = new Paint(); |
| (...skipping 285 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 877 return context.getString(R.string.remaining_duration_minutes, minute
s); | 883 return context.getString(R.string.remaining_duration_minutes, minute
s); |
| 878 } else if (minutes > 0) { | 884 } else if (minutes > 0) { |
| 879 return context.getString(R.string.remaining_duration_one_minute); | 885 return context.getString(R.string.remaining_duration_one_minute); |
| 880 } else if (seconds == 1) { | 886 } else if (seconds == 1) { |
| 881 return context.getString(R.string.remaining_duration_one_second); | 887 return context.getString(R.string.remaining_duration_one_second); |
| 882 } else { | 888 } else { |
| 883 return context.getString(R.string.remaining_duration_seconds, second
s); | 889 return context.getString(R.string.remaining_duration_seconds, second
s); |
| 884 } | 890 } |
| 885 } | 891 } |
| 886 } | 892 } |
| OLD | NEW |