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.annotation.TargetApi; | 7 import android.annotation.TargetApi; |
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; |
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
49 import org.chromium.chrome.browser.notifications.ChromeNotificationBuilder; | 49 import org.chromium.chrome.browser.notifications.ChromeNotificationBuilder; |
50 import org.chromium.chrome.browser.notifications.NotificationBuilderFactory; | 50 import org.chromium.chrome.browser.notifications.NotificationBuilderFactory; |
51 import org.chromium.chrome.browser.notifications.NotificationConstants; | 51 import org.chromium.chrome.browser.notifications.NotificationConstants; |
52 import org.chromium.chrome.browser.notifications.NotificationUmaTracker; | 52 import org.chromium.chrome.browser.notifications.NotificationUmaTracker; |
53 import org.chromium.chrome.browser.notifications.channels.ChannelDefinitions; | 53 import org.chromium.chrome.browser.notifications.channels.ChannelDefinitions; |
54 import org.chromium.chrome.browser.offlinepages.downloads.OfflinePageDownloadBri
dge; | 54 import org.chromium.chrome.browser.offlinepages.downloads.OfflinePageDownloadBri
dge; |
55 import org.chromium.chrome.browser.profiles.Profile; | 55 import org.chromium.chrome.browser.profiles.Profile; |
56 import org.chromium.chrome.browser.util.IntentUtils; | 56 import org.chromium.chrome.browser.util.IntentUtils; |
57 import org.chromium.components.offline_items_collection.ContentId; | 57 import org.chromium.components.offline_items_collection.ContentId; |
58 import org.chromium.components.offline_items_collection.LegacyHelpers; | 58 import org.chromium.components.offline_items_collection.LegacyHelpers; |
| 59 import org.chromium.components.offline_items_collection.OfflineItem.Progress; |
59 import org.chromium.content.browser.BrowserStartupController; | 60 import org.chromium.content.browser.BrowserStartupController; |
60 | 61 |
61 import java.util.ArrayList; | 62 import java.util.ArrayList; |
62 import java.util.List; | 63 import java.util.List; |
63 import java.util.concurrent.TimeUnit; | |
64 | 64 |
65 /** | 65 /** |
66 * Service responsible for creating and updating download notifications even aft
er | 66 * Service responsible for creating and updating download notifications even aft
er |
67 * Chrome gets killed. | 67 * Chrome gets killed. |
68 * | 68 * |
69 * On O and above, this service will receive {@link Service#startForeground(int,
Notification)} | 69 * On O and above, this service will receive {@link Service#startForeground(int,
Notification)} |
70 * calls when containing active downloads. The foreground notification will be
the summary | 70 * calls when containing active downloads. The foreground notification will be
the summary |
71 * notification generated by {@link DownloadNotificationService#buildSummaryNoti
fication(Context)}. | 71 * notification generated by {@link DownloadNotificationService#buildSummaryNoti
fication(Context)}. |
72 * The service will receive a {@link Service#stopForeground(boolean)} call when
all active downloads | 72 * The service will receive a {@link Service#stopForeground(boolean)} call when
all active downloads |
73 * are paused. The summary notification will be hidden when there are no other
notifications in the | 73 * are paused. The summary notification will be hidden when there are no other
notifications in the |
(...skipping 30 matching lines...) Expand all Loading... |
104 private static final String TAG = "DownloadNotification"; | 104 private static final String TAG = "DownloadNotification"; |
105 // Limit file name to 25 characters. TODO(qinmin): use different limit for d
ifferent devices? | 105 // Limit file name to 25 characters. TODO(qinmin): use different limit for d
ifferent devices? |
106 private static final int MAX_FILE_NAME_LENGTH = 25; | 106 private static final int MAX_FILE_NAME_LENGTH = 25; |
107 | 107 |
108 /** Notification Id starting value, to avoid conflicts from IDs used in prio
r versions. */ | 108 /** Notification Id starting value, to avoid conflicts from IDs used in prio
r versions. */ |
109 | 109 |
110 private static final String EXTRA_NOTIFICATION_BUNDLE_ICON_ID = | 110 private static final String EXTRA_NOTIFICATION_BUNDLE_ICON_ID = |
111 "Chrome.NotificationBundleIconIdExtra"; | 111 "Chrome.NotificationBundleIconIdExtra"; |
112 private static final int STARTING_NOTIFICATION_ID = 1000000; | 112 private static final int STARTING_NOTIFICATION_ID = 1000000; |
113 private static final int MAX_RESUMPTION_ATTEMPT_LEFT = 5; | 113 private static final int MAX_RESUMPTION_ATTEMPT_LEFT = 5; |
114 @VisibleForTesting static final long SECONDS_PER_MINUTE = TimeUnit.MINUTES.t
oSeconds(1); | |
115 @VisibleForTesting static final long SECONDS_PER_HOUR = TimeUnit.HOURS.toSec
onds(1); | |
116 @VisibleForTesting static final long SECONDS_PER_DAY = TimeUnit.DAYS.toSecon
ds(1); | |
117 | 114 |
118 private static final String KEY_AUTO_RESUMPTION_ATTEMPT_LEFT = "ResumptionAt
temptLeft"; | 115 private static final String KEY_AUTO_RESUMPTION_ATTEMPT_LEFT = "ResumptionAt
temptLeft"; |
119 private static final String KEY_NEXT_DOWNLOAD_NOTIFICATION_ID = "NextDownloa
dNotificationId"; | 116 private static final String KEY_NEXT_DOWNLOAD_NOTIFICATION_ID = "NextDownloa
dNotificationId"; |
120 | 117 |
121 /** | 118 /** |
122 * An Observer interface that allows other classes to know when this class i
s canceling | 119 * An Observer interface that allows other classes to know when this class i
s canceling |
123 * downloads. | 120 * downloads. |
124 */ | 121 */ |
125 public interface Observer { | 122 public interface Observer { |
126 /** | 123 /** |
(...skipping 604 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
731 SharedPreferences SharedPrefs = ContextUtils.getAppSharedPreferences(); | 728 SharedPreferences SharedPrefs = ContextUtils.getAppSharedPreferences(); |
732 SharedPreferences.Editor editor = SharedPrefs.edit(); | 729 SharedPreferences.Editor editor = SharedPrefs.edit(); |
733 editor.remove(KEY_AUTO_RESUMPTION_ATTEMPT_LEFT); | 730 editor.remove(KEY_AUTO_RESUMPTION_ATTEMPT_LEFT); |
734 editor.apply(); | 731 editor.apply(); |
735 } | 732 } |
736 | 733 |
737 /** | 734 /** |
738 * Adds or updates an in-progress download notification. | 735 * Adds or updates an in-progress download notification. |
739 * @param id The {@link ContentId} of the download. | 736 * @param id The {@link ContentId} of the download. |
740 * @param fileName File name of the download. | 737 * @param fileName File name of the download. |
741 * @param percentage Percentage completed. Value should be betw
een 0 to 100 if the | 738 * @param progress The current download progress. |
742 * percentage can be determined, or -1 if it
is unknown. | |
743 * @param bytesReceived Total number of bytes received. | 739 * @param bytesReceived Total number of bytes received. |
744 * @param timeRemainingInMillis Remaining download time in milliseconds. | 740 * @param timeRemainingInMillis Remaining download time in milliseconds. |
745 * @param startTime Time when download started. | 741 * @param startTime Time when download started. |
746 * @param isOffTheRecord Whether the download is off the record. | 742 * @param isOffTheRecord Whether the download is off the record. |
747 * @param canDownloadWhileMetered Whether the download can happen in metered
network. | 743 * @param canDownloadWhileMetered Whether the download can happen in metered
network. |
748 * @param isTransient Whether or not clicking on the download sh
ould launch | 744 * @param isTransient Whether or not clicking on the download sh
ould launch |
749 * downloads home. | 745 * downloads home. |
750 * @param icon A {@link Bitmap} to be used as the large i
con for display. | 746 * @param icon A {@link Bitmap} to be used as the large i
con for display. |
751 */ | 747 */ |
752 @VisibleForTesting | 748 @VisibleForTesting |
753 public void notifyDownloadProgress(ContentId id, String fileName, int percen
tage, | 749 public void notifyDownloadProgress(ContentId id, String fileName, Progress p
rogress, |
754 long bytesReceived, long timeRemainingInMillis, long startTime, bool
ean isOffTheRecord, | 750 long bytesReceived, long timeRemainingInMillis, long startTime, bool
ean isOffTheRecord, |
755 boolean canDownloadWhileMetered, boolean isTransient, Bitmap icon) { | 751 boolean canDownloadWhileMetered, boolean isTransient, Bitmap icon) { |
756 updateActiveDownloadNotification(id, fileName, percentage, bytesReceived
, | 752 updateActiveDownloadNotification(id, fileName, progress, bytesReceived, |
757 timeRemainingInMillis, startTime, isOffTheRecord, canDownloadWhi
leMetered, false, | 753 timeRemainingInMillis, startTime, isOffTheRecord, canDownloadWhi
leMetered, false, |
758 isTransient, icon); | 754 isTransient, icon); |
759 } | 755 } |
760 | 756 |
761 /** | 757 /** |
762 * Adds or updates a pending download notification. | 758 * Adds or updates a pending download notification. |
763 * @param id The {@link ContentId} of the download. | 759 * @param id The {@link ContentId} of the download. |
764 * @param fileName File name of the download. | 760 * @param fileName File name of the download. |
765 * @param isOffTheRecord Whether the download is off the record. | 761 * @param isOffTheRecord Whether the download is off the record. |
766 * @param canDownloadWhileMetered Whether the download can happen in metered
network. | 762 * @param canDownloadWhileMetered Whether the download can happen in metered
network. |
767 * @param isTransient Whether or not clicking on the download sh
ould launch | 763 * @param isTransient Whether or not clicking on the download sh
ould launch |
768 * downloads home. | 764 * downloads home. |
769 * @param icon A {@link Bitmap} to be used as the large i
con for display. | 765 * @param icon A {@link Bitmap} to be used as the large i
con for display. |
770 */ | 766 */ |
771 private void notifyDownloadPending(ContentId id, String fileName, boolean is
OffTheRecord, | 767 private void notifyDownloadPending(ContentId id, String fileName, boolean is
OffTheRecord, |
772 boolean canDownloadWhileMetered, boolean isTransient, Bitmap icon) { | 768 boolean canDownloadWhileMetered, boolean isTransient, Bitmap icon) { |
773 updateActiveDownloadNotification(id, fileName, | 769 updateActiveDownloadNotification(id, fileName, Progress.createIndetermin
ateProgress(), 0, 0, |
774 DownloadItem.INDETERMINATE_DOWNLOAD_PERCENTAGE, 0, 0, 0, isOffTh
eRecord, | 770 0, isOffTheRecord, canDownloadWhileMetered, true, isTransient, i
con); |
775 canDownloadWhileMetered, true, isTransient, icon); | |
776 } | 771 } |
777 | 772 |
778 /** | 773 /** |
779 * Helper method to update the notification for an active download, the down
load is either in | 774 * Helper method to update the notification for an active download, the down
load is either in |
780 * progress or pending. | 775 * progress or pending. |
781 * @param id The {@link ContentId} of the download. | 776 * @param id The {@link ContentId} of the download. |
782 * @param fileName File name of the download. | 777 * @param fileName File name of the download. |
783 * @param percentage Percentage completed. Value should be betw
een 0 to 100 if the | 778 * @param progress The current download progress. |
784 * percentage can be determined, or -1 if it
is unknown. | |
785 * @param bytesReceived Total number of bytes received. | 779 * @param bytesReceived Total number of bytes received. |
786 * @param timeRemainingInMillis Remaining download time in milliseconds or
-1 if it is | 780 * @param timeRemainingInMillis Remaining download time in milliseconds or
-1 if it is |
787 * unknown. | 781 * unknown. |
788 * @param startTime Time when download started. | 782 * @param startTime Time when download started. |
789 * @param isOffTheRecord Whether the download is off the record. | 783 * @param isOffTheRecord Whether the download is off the record. |
790 * @param canDownloadWhileMetered Whether the download can happen in metered
network. | 784 * @param canDownloadWhileMetered Whether the download can happen in metered
network. |
791 * @param isDownloadPending Whether the download is pending. | 785 * @param isDownloadPending Whether the download is pending. |
792 * @param isTransient Whether or not clicking on the download sh
ould launch | 786 * @param isTransient Whether or not clicking on the download sh
ould launch |
793 * downloads home. | 787 * downloads home. |
794 * @param icon A {@link Bitmap} to be used as the large i
con for display. | 788 * @param icon A {@link Bitmap} to be used as the large i
con for display. |
795 */ | 789 */ |
796 private void updateActiveDownloadNotification(ContentId id, String fileName,
int percentage, | 790 private void updateActiveDownloadNotification(ContentId id, String fileName,
Progress progress, |
797 long bytesReceived, long timeRemainingInMillis, long startTime, bool
ean isOffTheRecord, | 791 long bytesReceived, long timeRemainingInMillis, long startTime, bool
ean isOffTheRecord, |
798 boolean canDownloadWhileMetered, boolean isDownloadPending, boolean
isTransient, | 792 boolean canDownloadWhileMetered, boolean isDownloadPending, boolean
isTransient, |
799 Bitmap icon) { | 793 Bitmap icon) { |
800 boolean indeterminate = | 794 boolean indeterminate = (progress.isIndeterminate() || isDownloadPending
); |
801 (percentage == DownloadItem.INDETERMINATE_DOWNLOAD_PERCENTAGE) |
| isDownloadPending; | |
802 String contentText = null; | 795 String contentText = null; |
803 if (isDownloadPending) { | 796 if (isDownloadPending) { |
804 contentText = mContext.getResources().getString(R.string.download_no
tification_pending); | 797 contentText = mContext.getResources().getString(R.string.download_no
tification_pending); |
805 } else if (indeterminate) { | 798 } else if (indeterminate || timeRemainingInMillis < 0) { |
806 // TODO(dimich): Enable the byte count back in M59. See bug 704049 f
or more info and | 799 // TODO(dimich): Enable the byte count back in M59. See bug 704049 f
or more info and |
807 // details of what was temporarily reverted (for M58). | 800 // details of what was temporarily reverted (for M58). |
808 contentText = mContext.getResources().getString(R.string.download_st
arted); | 801 contentText = mContext.getResources().getString(R.string.download_st
arted); |
809 } else { | 802 } else { |
810 contentText = timeRemainingInMillis < 0 | 803 contentText = DownloadUtils.getTimeOrFilesLeftString( |
811 ? mContext.getResources().getString(R.string.download_starte
d) | 804 mContext, progress, timeRemainingInMillis); |
812 : formatRemainingTime(mContext, timeRemainingInMillis); | |
813 } | 805 } |
814 int resId = isDownloadPending ? R.drawable.ic_download_pending | 806 int resId = isDownloadPending ? R.drawable.ic_download_pending |
815 : android.R.drawable.stat_sys_download; | 807 : android.R.drawable.stat_sys_download; |
816 ChromeNotificationBuilder builder = buildNotification(resId, fileName, c
ontentText); | 808 ChromeNotificationBuilder builder = buildNotification(resId, fileName, c
ontentText); |
817 builder.setOngoing(true); | 809 builder.setOngoing(true); |
818 builder.setPriority(Notification.PRIORITY_HIGH); | 810 builder.setPriority(Notification.PRIORITY_HIGH); |
819 | 811 |
820 // Avoid animations while the download isn't progressing. | 812 // Avoid animations while the download isn't progressing. |
821 if (!isDownloadPending) { | 813 if (!isDownloadPending) { |
822 builder.setProgress(100, percentage, indeterminate); | 814 builder.setProgress(100, indeterminate ? -1 : progress.getPercentage
(), indeterminate); |
823 } | 815 } |
824 | 816 |
825 if (!indeterminate && !LegacyHelpers.isLegacyOfflinePage(id)) { | 817 if (!indeterminate && !LegacyHelpers.isLegacyOfflinePage(id)) { |
826 String percentText = DownloadUtils.getPercentageString(percentage); | 818 String percentText = DownloadUtils.getPercentageString(progress.getP
ercentage()); |
827 if (Build.VERSION.CODENAME.equals("N") | 819 if (Build.VERSION.CODENAME.equals("N") |
828 || Build.VERSION.SDK_INT > Build.VERSION_CODES.M) { | 820 || Build.VERSION.SDK_INT > Build.VERSION_CODES.M) { |
829 builder.setSubText(percentText); | 821 builder.setSubText(percentText); |
830 } else { | 822 } else { |
831 builder.setContentInfo(percentText); | 823 builder.setContentInfo(percentText); |
832 } | 824 } |
833 } | 825 } |
834 int notificationId = getNotificationId(id); | 826 int notificationId = getNotificationId(id); |
835 if (startTime > 0) builder.setWhen(startTime); | 827 if (startTime > 0) builder.setWhen(startTime); |
836 | 828 |
(...skipping 579 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1416 mDownloadSharedPreferenceHelper.getDownloadSharedPreferenceEntry
(id); | 1408 mDownloadSharedPreferenceHelper.getDownloadSharedPreferenceEntry
(id); |
1417 if (entry != null) return entry.notificationId; | 1409 if (entry != null) return entry.notificationId; |
1418 int notificationId = mNextNotificationId; | 1410 int notificationId = mNextNotificationId; |
1419 mNextNotificationId = mNextNotificationId == Integer.MAX_VALUE | 1411 mNextNotificationId = mNextNotificationId == Integer.MAX_VALUE |
1420 ? STARTING_NOTIFICATION_ID : mNextNotificationId + 1; | 1412 ? STARTING_NOTIFICATION_ID : mNextNotificationId + 1; |
1421 SharedPreferences.Editor editor = mSharedPrefs.edit(); | 1413 SharedPreferences.Editor editor = mSharedPrefs.edit(); |
1422 editor.putInt(KEY_NEXT_DOWNLOAD_NOTIFICATION_ID, mNextNotificationId); | 1414 editor.putInt(KEY_NEXT_DOWNLOAD_NOTIFICATION_ID, mNextNotificationId); |
1423 editor.apply(); | 1415 editor.apply(); |
1424 return notificationId; | 1416 return notificationId; |
1425 } | 1417 } |
1426 | |
1427 /** | |
1428 * Format remaining time for the given millis, in the following format: | |
1429 * 5 hours; will include 1 unit, can go down to seconds precision. | |
1430 * This is similar to what android.java.text.Formatter.formatShortElapsedTim
e() does. Don't use | |
1431 * ui::TimeFormat::Simple() as it is very expensive. | |
1432 * | |
1433 * @param context the application context. | |
1434 * @param millis the remaining time in milli seconds. | |
1435 * @return the formatted remaining time. | |
1436 */ | |
1437 public static String formatRemainingTime(Context context, long millis) { | |
1438 long secondsLong = millis / 1000; | |
1439 | |
1440 int days = 0; | |
1441 int hours = 0; | |
1442 int minutes = 0; | |
1443 if (secondsLong >= SECONDS_PER_DAY) { | |
1444 days = (int) (secondsLong / SECONDS_PER_DAY); | |
1445 secondsLong -= days * SECONDS_PER_DAY; | |
1446 } | |
1447 if (secondsLong >= SECONDS_PER_HOUR) { | |
1448 hours = (int) (secondsLong / SECONDS_PER_HOUR); | |
1449 secondsLong -= hours * SECONDS_PER_HOUR; | |
1450 } | |
1451 if (secondsLong >= SECONDS_PER_MINUTE) { | |
1452 minutes = (int) (secondsLong / SECONDS_PER_MINUTE); | |
1453 secondsLong -= minutes * SECONDS_PER_MINUTE; | |
1454 } | |
1455 int seconds = (int) secondsLong; | |
1456 | |
1457 if (days >= 2) { | |
1458 days += (hours + 12) / 24; | |
1459 return context.getString(R.string.remaining_duration_days, days); | |
1460 } else if (days > 0) { | |
1461 return context.getString(R.string.remaining_duration_one_day); | |
1462 } else if (hours >= 2) { | |
1463 hours += (minutes + 30) / 60; | |
1464 return context.getString(R.string.remaining_duration_hours, hours); | |
1465 } else if (hours > 0) { | |
1466 return context.getString(R.string.remaining_duration_one_hour); | |
1467 } else if (minutes >= 2) { | |
1468 minutes += (seconds + 30) / 60; | |
1469 return context.getString(R.string.remaining_duration_minutes, minute
s); | |
1470 } else if (minutes > 0) { | |
1471 return context.getString(R.string.remaining_duration_one_minute); | |
1472 } else if (seconds == 1) { | |
1473 return context.getString(R.string.remaining_duration_one_second); | |
1474 } else { | |
1475 return context.getString(R.string.remaining_duration_seconds, second
s); | |
1476 } | |
1477 } | |
1478 } | 1418 } |
OLD | NEW |