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; |
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.Bundle; | |
26 import android.os.IBinder; | 27 import android.os.IBinder; |
27 import android.service.notification.StatusBarNotification; | 28 import android.service.notification.StatusBarNotification; |
28 import android.text.TextUtils; | 29 import android.text.TextUtils; |
30 import android.util.Pair; | |
29 | 31 |
30 import org.chromium.base.ApiCompatibilityUtils; | 32 import org.chromium.base.ApiCompatibilityUtils; |
31 import org.chromium.base.ApplicationStatus; | 33 import org.chromium.base.ApplicationStatus; |
32 import org.chromium.base.BuildInfo; | 34 import org.chromium.base.BuildInfo; |
33 import org.chromium.base.ContextUtils; | 35 import org.chromium.base.ContextUtils; |
34 import org.chromium.base.Log; | 36 import org.chromium.base.Log; |
35 import org.chromium.base.ObserverList; | 37 import org.chromium.base.ObserverList; |
36 import org.chromium.base.VisibleForTesting; | 38 import org.chromium.base.VisibleForTesting; |
37 import org.chromium.base.library_loader.LibraryLoader; | 39 import org.chromium.base.library_loader.LibraryLoader; |
38 import org.chromium.base.library_loader.ProcessInitException; | 40 import org.chromium.base.library_loader.ProcessInitException; |
(...skipping 13 matching lines...) Expand all Loading... | |
52 import java.util.ArrayList; | 54 import java.util.ArrayList; |
53 import java.util.List; | 55 import java.util.List; |
54 import java.util.concurrent.TimeUnit; | 56 import java.util.concurrent.TimeUnit; |
55 | 57 |
56 /** | 58 /** |
57 * Service responsible for creating and updating download notifications even aft er | 59 * Service responsible for creating and updating download notifications even aft er |
58 * Chrome gets killed. | 60 * Chrome gets killed. |
59 * | 61 * |
60 * On O and above, this service will receive {@link Service#startForeground(int, Notification)} | 62 * On O and above, this service will receive {@link Service#startForeground(int, Notification)} |
61 * calls when containing active downloads. The foreground notification will be the summary | 63 * calls when containing active downloads. The foreground notification will be the summary |
62 * notification generated by {@link DownloadNotificationService#getSummaryNotifi cation(Context)}. | 64 * notification generated by {@link DownloadNotificationService#buildSummaryNoti fication(Context)}. |
63 * The service will receive a {@link Service#stopForeground(boolean)} call when all active downloads | 65 * The service will receive a {@link Service#stopForeground(boolean)} call when all active downloads |
64 * are paused. The summary notification will be hidden when there are no other notifications in the | 66 * are paused. The summary notification will be hidden when there are no other notifications in the |
65 * {@link NotificationConstants#GROUP_DOWNLOADS} group. This gets checked after every notification | 67 * {@link NotificationConstants#GROUP_DOWNLOADS} group. This gets checked after every notification |
66 * gets removed from the {@link NotificationManager}. | 68 * gets removed from the {@link NotificationManager}. |
67 */ | 69 */ |
68 public class DownloadNotificationService extends Service { | 70 public class DownloadNotificationService extends Service { |
69 static final String EXTRA_DOWNLOAD_GUID = "DownloadGuid"; | 71 static final String EXTRA_DOWNLOAD_GUID = "DownloadGuid"; |
70 static final String EXTRA_DOWNLOAD_FILE_PATH = "DownloadFilePath"; | 72 static final String EXTRA_DOWNLOAD_FILE_PATH = "DownloadFilePath"; |
71 static final String EXTRA_NOTIFICATION_DISMISSED = "NotificationDismissed"; | 73 static final String EXTRA_NOTIFICATION_DISMISSED = "NotificationDismissed"; |
72 static final String EXTRA_IS_SUPPORTED_MIME_TYPE = "IsSupportedMimeType"; | 74 static final String EXTRA_IS_SUPPORTED_MIME_TYPE = "IsSupportedMimeType"; |
73 static final String EXTRA_IS_OFF_THE_RECORD = | 75 static final String EXTRA_IS_OFF_THE_RECORD = |
74 "org.chromium.chrome.browser.download.IS_OFF_THE_RECORD"; | 76 "org.chromium.chrome.browser.download.IS_OFF_THE_RECORD"; |
75 static final String EXTRA_IS_OFFLINE_PAGE = | 77 static final String EXTRA_IS_OFFLINE_PAGE = |
76 "org.chromium.chrome.browser.download.IS_OFFLINE_PAGE"; | 78 "org.chromium.chrome.browser.download.IS_OFFLINE_PAGE"; |
77 | 79 |
78 public static final String ACTION_DOWNLOAD_CANCEL = | 80 public static final String ACTION_DOWNLOAD_CANCEL = |
79 "org.chromium.chrome.browser.download.DOWNLOAD_CANCEL"; | 81 "org.chromium.chrome.browser.download.DOWNLOAD_CANCEL"; |
80 public static final String ACTION_DOWNLOAD_PAUSE = | 82 public static final String ACTION_DOWNLOAD_PAUSE = |
81 "org.chromium.chrome.browser.download.DOWNLOAD_PAUSE"; | 83 "org.chromium.chrome.browser.download.DOWNLOAD_PAUSE"; |
82 public static final String ACTION_DOWNLOAD_RESUME = | 84 public static final String ACTION_DOWNLOAD_RESUME = |
83 "org.chromium.chrome.browser.download.DOWNLOAD_RESUME"; | 85 "org.chromium.chrome.browser.download.DOWNLOAD_RESUME"; |
84 static final String ACTION_DOWNLOAD_RESUME_ALL = | 86 static final String ACTION_DOWNLOAD_RESUME_ALL = |
85 "org.chromium.chrome.browser.download.DOWNLOAD_RESUME_ALL"; | 87 "org.chromium.chrome.browser.download.DOWNLOAD_RESUME_ALL"; |
86 public static final String ACTION_DOWNLOAD_OPEN = | 88 public static final String ACTION_DOWNLOAD_OPEN = |
87 "org.chromium.chrome.browser.download.DOWNLOAD_OPEN"; | 89 "org.chromium.chrome.browser.download.DOWNLOAD_OPEN"; |
90 public static final String ACTION_UPDATE_SUMMARY_ICON = | |
91 "org.chromium.chrome.browser.download.UPDATE_SUMMARY_ICON"; | |
88 | 92 |
89 static final String NOTIFICATION_NAMESPACE = "DownloadNotificationService"; | 93 static final String NOTIFICATION_NAMESPACE = "DownloadNotificationService"; |
90 private static final String TAG = "DownloadNotification"; | 94 private static final String TAG = "DownloadNotification"; |
91 // Limit file name to 25 characters. TODO(qinmin): use different limit for d ifferent devices? | 95 // Limit file name to 25 characters. TODO(qinmin): use different limit for d ifferent devices? |
92 private static final int MAX_FILE_NAME_LENGTH = 25; | 96 private static final int MAX_FILE_NAME_LENGTH = 25; |
93 | 97 |
94 /** Notification Id starting value, to avoid conflicts from IDs used in prio r versions. */ | 98 /** Notification Id starting value, to avoid conflicts from IDs used in prio r versions. */ |
95 | 99 |
100 private static final String EXTRA_NOTIFICATION_BUNDLE_ICON_ID = | |
101 "Chrome.NotificationBundleIconIdExtra"; | |
96 private static final int STARTING_NOTIFICATION_ID = 1000000; | 102 private static final int STARTING_NOTIFICATION_ID = 1000000; |
97 private static final int MAX_RESUMPTION_ATTEMPT_LEFT = 5; | 103 private static final int MAX_RESUMPTION_ATTEMPT_LEFT = 5; |
98 @VisibleForTesting static final long SECONDS_PER_MINUTE = TimeUnit.MINUTES.t oSeconds(1); | 104 @VisibleForTesting static final long SECONDS_PER_MINUTE = TimeUnit.MINUTES.t oSeconds(1); |
99 @VisibleForTesting static final long SECONDS_PER_HOUR = TimeUnit.HOURS.toSec onds(1); | 105 @VisibleForTesting static final long SECONDS_PER_HOUR = TimeUnit.HOURS.toSec onds(1); |
100 @VisibleForTesting static final long SECONDS_PER_DAY = TimeUnit.DAYS.toSecon ds(1); | 106 @VisibleForTesting static final long SECONDS_PER_DAY = TimeUnit.DAYS.toSecon ds(1); |
101 | 107 |
102 private static final String KEY_AUTO_RESUMPTION_ATTEMPT_LEFT = "ResumptionAt temptLeft"; | 108 private static final String KEY_AUTO_RESUMPTION_ATTEMPT_LEFT = "ResumptionAt temptLeft"; |
103 private static final String KEY_NEXT_DOWNLOAD_NOTIFICATION_ID = "NextDownloa dNotificationId"; | 109 private static final String KEY_NEXT_DOWNLOAD_NOTIFICATION_ID = "NextDownloa dNotificationId"; |
104 | 110 |
105 /** | 111 /** |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
138 * Start this service with a summary {@link Notification}. This will start the service in the | 144 * Start this service with a summary {@link Notification}. This will start the service in the |
139 * foreground. | 145 * foreground. |
140 * @param context The context used to build the notification and to start th e service. | 146 * @param context The context used to build the notification and to start th e service. |
141 * @param source The {@link Intent} that should be used to build on to start the service. | 147 * @param source The {@link Intent} that should be used to build on to start the service. |
142 */ | 148 */ |
143 public static void startDownloadNotificationService(Context context, Intent source) { | 149 public static void startDownloadNotificationService(Context context, Intent source) { |
144 Intent intent = source != null ? new Intent(source) : new Intent(); | 150 Intent intent = source != null ? new Intent(source) : new Intent(); |
145 intent.setComponent(new ComponentName(context, DownloadNotificationServi ce.class)); | 151 intent.setComponent(new ComponentName(context, DownloadNotificationServi ce.class)); |
146 | 152 |
147 if (BuildInfo.isAtLeastO()) { | 153 if (BuildInfo.isAtLeastO()) { |
148 Notification notification = getSummaryNotification(context); | 154 Notification notification = buildSummaryNotification(context); |
149 | 155 |
150 AppHooks.get().startServiceWithNotification( | 156 AppHooks.get().startServiceWithNotification( |
151 intent, NotificationConstants.NOTIFICATION_ID_DOWNLOAD_SUMMA RY, notification); | 157 intent, NotificationConstants.NOTIFICATION_ID_DOWNLOAD_SUMMA RY, notification); |
152 } else { | 158 } else { |
153 context.startService(intent); | 159 context.startService(intent); |
154 } | 160 } |
155 } | 161 } |
156 | 162 |
157 /** | 163 /** |
158 * Builds a summary notification that represents downloads. This is the not ification passed to | 164 * Calculates the suggested icon for the summary notification based on the o ther notifications |
159 * {@link #startForeground(int, Notification)}, which keeps this service in the foreground. | 165 * currently showing. |
160 * @param context The context used to build the notification and pull specif ic resources. | 166 * @param context A context to use to query Android-specific information (No tificationManager). |
161 * @return The {@link Notification} to show for the summary. Meant to be us ed by | 167 * @param removedNotificationId The id of a notification that is currently c losing and should be |
162 * {@link NotificationManager#notify(int, Notification)}. | 168 * ignored. -1 if no notifications are being c losed. |
169 * @param addedNotification A {@link Pair} of <id, Notification> of a no tification that is | |
170 * currently being added and should be used in addition to or in | |
171 * place of the existing icons. | |
172 * @return A {@link Pair} that represents both whether or not the new icon | |
173 * is different from the old one and the icon i d itself. | |
163 */ | 174 */ |
164 private static Notification getSummaryNotification(Context context) { | 175 @TargetApi(Build.VERSION_CODES.M) |
176 private static Pair<Boolean, Integer> getSummaryIcon(Context context, int re movedNotificationId, | |
177 Pair<Integer, Notification> addedNotification) { | |
178 if (!BuildInfo.isAtLeastO()) return new Pair<Boolean, Integer>(false, -1 ); | |
179 boolean progress = false; | |
180 boolean paused = false; | |
181 boolean pending = false; | |
182 boolean completed = false; | |
183 boolean failed = false; | |
184 | |
185 final int progressIcon = android.R.drawable.stat_sys_download; | |
186 final int pausedIcon = R.drawable.ic_download_pause; | |
187 final int pendingIcon = R.drawable.ic_download_pending; | |
188 final int completedIcon = R.drawable.offline_pin; | |
189 final int failedIcon = android.R.drawable.stat_sys_download_done; | |
190 | |
191 NotificationManager manager = | |
192 (NotificationManager) context.getSystemService(Context.NOTIFICAT ION_SERVICE); | |
193 StatusBarNotification[] notifications = manager.getActiveNotifications() ; | |
194 | |
195 int oldIcon = -1; | |
196 for (StatusBarNotification notification : notifications) { | |
197 boolean isDownloadsGroup = TextUtils.equals(notification.getNotifica tion().getGroup(), | |
198 NotificationConstants.GROUP_DOWNLOADS); | |
199 if (!isDownloadsGroup) continue; | |
200 if (notification.getId() == removedNotificationId) continue; | |
201 | |
202 boolean isSummaryNotification = | |
203 notification.getId() == NotificationConstants.NOTIFICATION_I D_DOWNLOAD_SUMMARY; | |
204 | |
205 if (addedNotification != null && addedNotification.first == notifica tion.getId()) | |
qinmin
2017/03/05 06:48:26
if addedNotification is not null, this for loop sk
David Trainor- moved to gerrit
2017/03/06 16:29:54
The added notification might not be shown yet, so
qinmin
2017/03/06 21:43:43
i see, in that case, can we just initialize those
| |
206 continue; | |
207 | |
208 int icon = | |
209 notification.getNotification().extras.getInt(EXTRA_NOTIFICAT ION_BUNDLE_ICON_ID); | |
210 if (isSummaryNotification) { | |
211 oldIcon = icon; | |
212 continue; | |
213 } | |
214 | |
215 progress |= icon == progressIcon; | |
216 paused |= icon == pausedIcon; | |
217 pending |= icon == pendingIcon; | |
218 completed |= icon == completedIcon; | |
219 failed |= icon == failedIcon; | |
220 } | |
221 | |
222 if (addedNotification != null) { | |
223 int icon = addedNotification.second.extras.getInt(EXTRA_NOTIFICATION _BUNDLE_ICON_ID); | |
224 | |
225 progress |= icon == progressIcon; | |
226 paused |= icon == pausedIcon; | |
227 pending |= icon == pendingIcon; | |
228 completed |= icon == completedIcon; | |
229 failed |= icon == failedIcon; | |
230 } | |
231 | |
232 int newIcon = android.R.drawable.stat_sys_download_done; | |
233 if (progress) { | |
234 newIcon = android.R.drawable.stat_sys_download; | |
235 } else if (paused) { | |
236 newIcon = R.drawable.ic_download_pause; | |
237 } else if (pending) { | |
238 newIcon = R.drawable.ic_download_pending; | |
239 } else if (completed) { | |
240 newIcon = R.drawable.offline_pin; | |
241 } else if (failed) { | |
242 newIcon = android.R.drawable.stat_sys_download_done; | |
243 } | |
244 return new Pair<Boolean, Integer>(newIcon != oldIcon, newIcon); | |
245 } | |
246 | |
247 /** | |
248 * Builds a summary notification that represents all downloads. | |
249 * {@see #buildSummaryNotification(Context)}. | |
250 * @param context A context used to query Android strings and resources. | |
251 * @param iconId The id of an icon to use for the notification. | |
252 * @return a {@link Notification} that represents the summary icon fo r all downloads. | |
253 */ | |
254 private static Notification buildSummaryNotificationWithIcon(Context context , int iconId) { | |
165 String title = | 255 String title = |
166 context.getResources().getString(R.string.download_notification_ summary_title, | 256 context.getResources().getString(R.string.download_notification_ summary_title, |
167 DownloadSharedPreferenceHelper.getInstance().getEntries( ).size()); | 257 DownloadSharedPreferenceHelper.getInstance().getEntries( ).size()); |
258 | |
168 ChromeNotificationBuilder builder = | 259 ChromeNotificationBuilder builder = |
169 AppHooks.get() | 260 AppHooks.get() |
170 .createChromeNotificationBuilder(true /* preferCompat */ , | 261 .createChromeNotificationBuilder(true /* preferCompat */ , |
171 NotificationConstants.CATEGORY_ID_BROWSER, | 262 NotificationConstants.CATEGORY_ID_BROWSER, |
172 context.getString(R.string.notification_category _browser), | 263 context.getString(R.string.notification_category _browser), |
173 NotificationConstants.CATEGORY_GROUP_ID_GENERAL, | 264 NotificationConstants.CATEGORY_GROUP_ID_GENERAL, |
174 context.getString(R.string.notification_category _group_general)) | 265 context.getString(R.string.notification_category _group_general)) |
175 .setContentTitle(title) | 266 .setContentTitle(title) |
176 .setSmallIcon(android.R.drawable.stat_sys_download_done) | 267 .setSmallIcon(iconId) |
177 .setLocalOnly(true) | 268 .setLocalOnly(true) |
178 .setGroup(NotificationConstants.GROUP_DOWNLOADS) | 269 .setGroup(NotificationConstants.GROUP_DOWNLOADS) |
179 .setGroupSummary(true); | 270 .setGroupSummary(true); |
271 Bundle extras = new Bundle(); | |
272 extras.putInt(EXTRA_NOTIFICATION_BUNDLE_ICON_ID, iconId); | |
273 builder.addExtras(extras); | |
180 | 274 |
181 // This notification should not actually be shown. But if it is, set th e click intent to | 275 // This notification should not actually be shown. But if it is, set th e click intent to |
182 // open downloads home. | 276 // open downloads home. |
183 Intent downloadHomeIntent = buildActionIntent( | 277 Intent downloadHomeIntent = buildActionIntent( |
184 context, DownloadManager.ACTION_NOTIFICATION_CLICKED, null, fals e, false); | 278 context, DownloadManager.ACTION_NOTIFICATION_CLICKED, null, fals e, false); |
185 builder.setContentIntent(PendingIntent.getBroadcast(context, | 279 builder.setContentIntent(PendingIntent.getBroadcast(context, |
186 NotificationConstants.NOTIFICATION_ID_DOWNLOAD_SUMMARY, download HomeIntent, | 280 NotificationConstants.NOTIFICATION_ID_DOWNLOAD_SUMMARY, download HomeIntent, |
187 PendingIntent.FLAG_UPDATE_CURRENT)); | 281 PendingIntent.FLAG_UPDATE_CURRENT)); |
188 | 282 |
189 return builder.build(); | 283 return builder.build(); |
190 } | 284 } |
191 | 285 |
192 /** | 286 /** |
287 * Builds a summary notification that represents downloads. This is the not ification passed to | |
288 * {@link #startForeground(int, Notification)}, which keeps this service in the foreground. | |
289 * @param context The context used to build the notification and pull specif ic resources. | |
290 * @return The {@link Notification} to show for the summary. Meant to be us ed by | |
291 * {@link NotificationManager#notify(int, Notification)}. | |
292 */ | |
293 private static Notification buildSummaryNotification(Context context) { | |
294 Pair<Boolean, Integer> icon = getSummaryIcon(context, -1, null); | |
295 return buildSummaryNotificationWithIcon(context, icon.second); | |
296 } | |
297 | |
298 /** | |
193 * @return Whether or not there are any current resumable downloads being tr acked. These | 299 * @return Whether or not there are any current resumable downloads being tr acked. These |
194 * tracked downloads may not currently be showing notifications. | 300 * tracked downloads may not currently be showing notifications. |
195 */ | 301 */ |
196 public static boolean isTrackingResumableDownloads(Context context) { | 302 public static boolean isTrackingResumableDownloads(Context context) { |
197 List<DownloadSharedPreferenceEntry> entries = | 303 List<DownloadSharedPreferenceEntry> entries = |
198 DownloadSharedPreferenceHelper.getInstance().getEntries(); | 304 DownloadSharedPreferenceHelper.getInstance().getEntries(); |
199 for (DownloadSharedPreferenceEntry entry : entries) { | 305 for (DownloadSharedPreferenceEntry entry : entries) { |
200 if (canResumeDownload(context, entry)) return true; | 306 if (canResumeDownload(context, entry)) return true; |
201 } | 307 } |
202 return false; | 308 return false; |
(...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
261 if (ACTION_DOWNLOAD_RESUME_ALL.equals(intent.getAction())) { | 367 if (ACTION_DOWNLOAD_RESUME_ALL.equals(intent.getAction())) { |
262 if (mNumAutoResumptionAttemptLeft > 0) { | 368 if (mNumAutoResumptionAttemptLeft > 0) { |
263 mNumAutoResumptionAttemptLeft--; | 369 mNumAutoResumptionAttemptLeft--; |
264 updateResumptionAttemptLeft(); | 370 updateResumptionAttemptLeft(); |
265 } | 371 } |
266 } else { | 372 } else { |
267 // Reset number of attempts left if the action is triggered by u ser. | 373 // Reset number of attempts left if the action is triggered by u ser. |
268 mNumAutoResumptionAttemptLeft = MAX_RESUMPTION_ATTEMPT_LEFT; | 374 mNumAutoResumptionAttemptLeft = MAX_RESUMPTION_ATTEMPT_LEFT; |
269 clearResumptionAttemptLeft(); | 375 clearResumptionAttemptLeft(); |
270 } | 376 } |
377 } else if (ACTION_UPDATE_SUMMARY_ICON.equals(intent.getAction())) { | |
378 updateSummaryIcon(-1, null); | |
379 hideSummaryNotificationIfNecessary(null); | |
271 } | 380 } |
272 // This should restart the service after Chrome gets killed. However, th is | 381 // This should restart the service after Chrome gets killed. However, th is |
273 // doesn't work on Android 4.4.2. | 382 // doesn't work on Android 4.4.2. |
274 return START_STICKY; | 383 return START_STICKY; |
275 } | 384 } |
276 | 385 |
277 /** | 386 /** |
278 * Adds an {@link Observer}, which will be notified when this service attemp ts to | 387 * Adds an {@link Observer}, which will be notified when this service attemp ts to |
279 * start stopping itself. | 388 * start stopping itself. |
280 */ | 389 */ |
(...skipping 17 matching lines...) Expand all Loading... | |
298 */ | 407 */ |
299 @VisibleForTesting | 408 @VisibleForTesting |
300 @TargetApi(Build.VERSION_CODES.N) | 409 @TargetApi(Build.VERSION_CODES.N) |
301 void stopForegroundInteral(boolean killNotification) { | 410 void stopForegroundInteral(boolean killNotification) { |
302 if (!BuildInfo.isAtLeastO()) return; | 411 if (!BuildInfo.isAtLeastO()) return; |
303 stopForeground(killNotification ? STOP_FOREGROUND_REMOVE : STOP_FOREGROU ND_DETACH); | 412 stopForeground(killNotification ? STOP_FOREGROUND_REMOVE : STOP_FOREGROU ND_DETACH); |
304 } | 413 } |
305 | 414 |
306 /** | 415 /** |
307 * On >= O Android releases, puts this service into a foreground state, bind ing it to the | 416 * On >= O Android releases, puts this service into a foreground state, bind ing it to the |
308 * {@link Notification} generated by {@link #getSummaryNotification(Context) }. | 417 * {@link Notification} generated by {@link #buildSummaryNotification(Contex t)}. |
309 */ | 418 */ |
310 @VisibleForTesting | 419 @VisibleForTesting |
311 void startForegroundInternal() { | 420 void startForegroundInternal() { |
312 if (!BuildInfo.isAtLeastO()) return; | 421 if (!BuildInfo.isAtLeastO()) return; |
313 Notification notification = getSummaryNotification(getApplicationContext ()); | 422 Notification notification = buildSummaryNotification(getApplicationConte xt()); |
314 startForeground(NotificationConstants.NOTIFICATION_ID_DOWNLOAD_SUMMARY, notification); | 423 startForeground(NotificationConstants.NOTIFICATION_ID_DOWNLOAD_SUMMARY, notification); |
315 } | 424 } |
316 | 425 |
317 private void rescheduleDownloads() { | 426 private void rescheduleDownloads() { |
318 List<DownloadSharedPreferenceEntry> entries = mDownloadSharedPreferenceH elper.getEntries(); | 427 List<DownloadSharedPreferenceEntry> entries = mDownloadSharedPreferenceH elper.getEntries(); |
319 if (entries.isEmpty()) return; | 428 if (entries.isEmpty()) return; |
320 | 429 |
321 boolean scheduleAutoResumption = false; | 430 boolean scheduleAutoResumption = false; |
322 boolean allowMeteredConnection = false; | 431 boolean allowMeteredConnection = false; |
323 for (int i = 0; i < entries.size(); ++i) { | 432 for (int i = 0; i < entries.size(); ++i) { |
(...skipping 110 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
434 NotificationConstants.GROUP_DOWNLOADS); | 543 NotificationConstants.GROUP_DOWNLOADS); |
435 boolean isSummaryNotification = | 544 boolean isSummaryNotification = |
436 notification.getId() == NotificationConstants.NOTIFICATION_I D_DOWNLOAD_SUMMARY; | 545 notification.getId() == NotificationConstants.NOTIFICATION_I D_DOWNLOAD_SUMMARY; |
437 if (isDownloadsGroup && isSummaryNotification) return notification; | 546 if (isDownloadsGroup && isSummaryNotification) return notification; |
438 } | 547 } |
439 | 548 |
440 return null; | 549 return null; |
441 } | 550 } |
442 | 551 |
443 /** | 552 /** |
553 * Updates the notification summary with a new icon, if necessary. | |
554 * @param removedNotificationId The id of a notification that is currently c losing and should be | |
555 * ignored. -1 if no notifications are being c losed. | |
556 * @param addedNotification A {@link Pair} of <id, Notification> of a no tification that is | |
557 * currently being added and should be used in addition to or in | |
558 * place of the existing icons. | |
559 */ | |
560 @VisibleForTesting | |
561 void updateSummaryIcon( | |
562 int removedNotificationId, Pair<Integer, Notification> addedNotifica tion) { | |
563 if (!BuildInfo.isAtLeastO()) return; | |
564 | |
565 Pair<Boolean, Integer> icon = | |
566 getSummaryIcon(mContext, removedNotificationId, addedNotificatio n); | |
567 | |
568 // Avoid rebuilding the summary notification if the icon hasn't changed or we have (or are | |
569 // about to have) no active downloads. | |
570 if (!icon.first || !hasDownloadNotifications(removedNotificationId)) { | |
571 return; | |
572 } | |
573 | |
574 mNotificationManager.notify(NotificationConstants.NOTIFICATION_ID_DOWNLO AD_SUMMARY, | |
575 buildSummaryNotificationWithIcon(mContext, icon.second)); | |
576 } | |
577 | |
578 /** | |
444 * Cancels the existing summary notification. Moved to a helper method for test mocking. | 579 * Cancels the existing summary notification. Moved to a helper method for test mocking. |
445 */ | 580 */ |
446 @VisibleForTesting | 581 @VisibleForTesting |
447 void cancelSummaryNotification() { | 582 void cancelSummaryNotification() { |
448 mNotificationManager.cancel(NotificationConstants.NOTIFICATION_ID_DOWNLO AD_SUMMARY); | 583 mNotificationManager.cancel(NotificationConstants.NOTIFICATION_ID_DOWNLO AD_SUMMARY); |
449 } | 584 } |
450 | 585 |
451 /** | 586 /** |
452 * Check all current notifications and hide the summary notification if we h ave no downloads | 587 * Check all current notifications and hide the summary notification if we h ave no downloads |
453 * notifications left. On Android if the user swipes away the last download notification the | 588 * notifications left. On Android if the user swipes away the last download notification the |
454 * summary will be dismissed. But if the last downloads notification is dis missed via | 589 * summary will be dismissed. But if the last downloads notification is dis missed via |
455 * {@link NotificationManager#cancel(int)}, the summary will remain, so we n eed to check and | 590 * {@link NotificationManager#cancel(int)}, the summary will remain, so we n eed to check and |
456 * manually remove it ourselves. | 591 * manually remove it ourselves. |
457 * @param notificationIdToIgnore Canceling a notification and querying for t he current list of | 592 * @param notificationIdToIgnore Canceling a notification and querying for t he current list of |
458 * active notifications isn't synchronous. Pa ss a notification id | 593 * active notifications isn't synchronous. Pa ss a notification id |
459 * here if there is a notification that should be assumed gone. | 594 * here if there is a notification that should be assumed gone. |
460 * Or pass {@code null} if no notification fit s that criteria. | 595 * Or pass {@code null} if no notification fit s that criteria. |
461 */ | 596 */ |
462 @TargetApi(Build.VERSION_CODES.M) | 597 @TargetApi(Build.VERSION_CODES.M) |
463 void hideSummaryNotificationIfNecessary(Integer notificationIdToIgnore) { | 598 boolean hideSummaryNotificationIfNecessary(Integer notificationIdToIgnore) { |
464 if (!BuildInfo.isAtLeastO()) return; | 599 if (!BuildInfo.isAtLeastO()) return false; |
465 if (mDownloadsInProgress.size() > 0) return; | 600 if (mDownloadsInProgress.size() > 0) return false; |
466 | 601 |
467 if (hasDownloadNotifications(notificationIdToIgnore)) return; | 602 if (hasDownloadNotifications(notificationIdToIgnore)) return false; |
468 | 603 |
469 StatusBarNotification notification = getSummaryNotification(); | 604 StatusBarNotification notification = getSummaryNotification(); |
470 if (notification != null) { | 605 if (notification != null) { |
471 // We have a valid summary notification, but how we dismiss it depen ds on whether or not | 606 // We have a valid summary notification, but how we dismiss it depen ds on whether or not |
472 // it is currently bound to this service via startForeground(...). | 607 // it is currently bound to this service via startForeground(...). |
473 if ((notification.getNotification().flags & Notification.FLAG_FOREGR OUND_SERVICE) | 608 if ((notification.getNotification().flags & Notification.FLAG_FOREGR OUND_SERVICE) |
474 != 0) { | 609 != 0) { |
475 // If we are a foreground service and we are hiding the notifica tion, we have no | 610 // If we are a foreground service and we are hiding the notifica tion, we have no |
476 // other downloads notifications showing, so we need to remove t he notification and | 611 // other downloads notifications showing, so we need to remove t he notification and |
477 // unregister it from this service at the same time. | 612 // unregister it from this service at the same time. |
(...skipping 10 matching lines...) Expand all Loading... | |
488 stopForegroundInteral(false); | 623 stopForegroundInteral(false); |
489 } | 624 } |
490 | 625 |
491 // Notify all observers that we are requesting a chance to shut down. T his will let any | 626 // Notify all observers that we are requesting a chance to shut down. T his will let any |
492 // observers unbind from us if necessary. | 627 // observers unbind from us if necessary. |
493 for (Observer observer : mObservers) observer.onServiceShutdownRequested (); | 628 for (Observer observer : mObservers) observer.onServiceShutdownRequested (); |
494 | 629 |
495 // Stop the service which should start the destruction process. At this point we should be | 630 // Stop the service which should start the destruction process. At this point we should be |
496 // (1) a background service and (2) unbound from any clients. | 631 // (1) a background service and (2) unbound from any clients. |
497 stopSelf(); | 632 stopSelf(); |
633 return true; | |
498 } | 634 } |
499 | 635 |
500 @Override | 636 @Override |
501 public IBinder onBind(Intent intent) { | 637 public IBinder onBind(Intent intent) { |
502 return mBinder; | 638 return mBinder; |
503 } | 639 } |
504 | 640 |
505 /** | 641 /** |
506 * Helper method to update the remaining number of background resumption att empts left. | 642 * Helper method to update the remaining number of background resumption att empts left. |
507 */ | 643 */ |
(...skipping 139 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
647 */ | 783 */ |
648 @VisibleForTesting | 784 @VisibleForTesting |
649 void cancelNotification(int notificationId, String downloadGuid) { | 785 void cancelNotification(int notificationId, String downloadGuid) { |
650 mNotificationManager.cancel(NOTIFICATION_NAMESPACE, notificationId); | 786 mNotificationManager.cancel(NOTIFICATION_NAMESPACE, notificationId); |
651 mDownloadSharedPreferenceHelper.removeSharedPreferenceEntry(downloadGuid ); | 787 mDownloadSharedPreferenceHelper.removeSharedPreferenceEntry(downloadGuid ); |
652 | 788 |
653 // Since we are about to go through the process of validating whether or not we can shut | 789 // Since we are about to go through the process of validating whether or not we can shut |
654 // down, don't stop foreground if we have no download notifications left to show. Hiding | 790 // down, don't stop foreground if we have no download notifications left to show. Hiding |
655 // the summary will take care of that for us. | 791 // the summary will take care of that for us. |
656 stopTrackingInProgressDownload(downloadGuid, hasDownloadNotifications(no tificationId)); | 792 stopTrackingInProgressDownload(downloadGuid, hasDownloadNotifications(no tificationId)); |
657 hideSummaryNotificationIfNecessary(notificationId); | 793 if (!hideSummaryNotificationIfNecessary(notificationId)) { |
794 updateSummaryIcon(notificationId, null); | |
795 } | |
658 } | 796 } |
659 | 797 |
660 /** | 798 /** |
661 * Called when a download is canceled. | 799 * Called when a download is canceled. |
662 * @param downloadGuid GUID of the download. | 800 * @param downloadGuid GUID of the download. |
663 */ | 801 */ |
664 @VisibleForTesting | 802 @VisibleForTesting |
665 public void notifyDownloadCanceled(String downloadGuid) { | 803 public void notifyDownloadCanceled(String downloadGuid) { |
666 DownloadSharedPreferenceEntry entry = | 804 DownloadSharedPreferenceEntry entry = |
667 mDownloadSharedPreferenceHelper.getDownloadSharedPreferenceEntry (downloadGuid); | 805 mDownloadSharedPreferenceHelper.getDownloadSharedPreferenceEntry (downloadGuid); |
(...skipping 94 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
762 } | 900 } |
763 intent.setComponent(component); | 901 intent.setComponent(component); |
764 builder.setContentIntent(PendingIntent.getBroadcast( | 902 builder.setContentIntent(PendingIntent.getBroadcast( |
765 mContext, notificationId, intent, PendingIntent.FLAG_UPDATE_CURR ENT)); | 903 mContext, notificationId, intent, PendingIntent.FLAG_UPDATE_CURR ENT)); |
766 if (mDownloadSuccessLargeIcon == null) { | 904 if (mDownloadSuccessLargeIcon == null) { |
767 Bitmap bitmap = BitmapFactory.decodeResource( | 905 Bitmap bitmap = BitmapFactory.decodeResource( |
768 mContext.getResources(), R.drawable.offline_pin); | 906 mContext.getResources(), R.drawable.offline_pin); |
769 mDownloadSuccessLargeIcon = getLargeNotificationIcon(bitmap); | 907 mDownloadSuccessLargeIcon = getLargeNotificationIcon(bitmap); |
770 } | 908 } |
771 builder.setLargeIcon(mDownloadSuccessLargeIcon); | 909 builder.setLargeIcon(mDownloadSuccessLargeIcon); |
910 builder.setDeleteIntent(buildIconUpdateIntent(notificationId)); | |
772 updateNotification(notificationId, builder.build(), downloadGuid, isOffl inePage, null); | 911 updateNotification(notificationId, builder.build(), downloadGuid, isOffl inePage, null); |
773 stopTrackingInProgressDownload(downloadGuid, true); | 912 stopTrackingInProgressDownload(downloadGuid, true); |
774 return notificationId; | 913 return notificationId; |
775 } | 914 } |
776 | 915 |
777 /** | 916 /** |
778 * Add a download failed notification. | 917 * Add a download failed notification. |
779 * @param isOfflinePage Whether or not the download was for an offline page. | 918 * @param isOfflinePage Whether or not the download was for an offline page. |
780 * @param downloadGuid GUID of the download. | 919 * @param downloadGuid GUID of the download. |
781 * @param fileName GUID of the download. | 920 * @param fileName GUID of the download. |
782 */ | 921 */ |
783 @VisibleForTesting | 922 @VisibleForTesting |
784 public void notifyDownloadFailed(boolean isOfflinePage, String downloadGuid, String fileName) { | 923 public void notifyDownloadFailed(boolean isOfflinePage, String downloadGuid, String fileName) { |
785 // If the download is not in history db, fileName could be empty. Get it from | 924 // If the download is not in history db, fileName could be empty. Get it from |
786 // SharedPreferences. | 925 // SharedPreferences. |
787 if (TextUtils.isEmpty(fileName)) { | 926 if (TextUtils.isEmpty(fileName)) { |
788 DownloadSharedPreferenceEntry entry = | 927 DownloadSharedPreferenceEntry entry = |
789 mDownloadSharedPreferenceHelper.getDownloadSharedPreferenceE ntry(downloadGuid); | 928 mDownloadSharedPreferenceHelper.getDownloadSharedPreferenceE ntry(downloadGuid); |
790 if (entry == null) return; | 929 if (entry == null) return; |
791 fileName = entry.fileName; | 930 fileName = entry.fileName; |
792 } | 931 } |
793 | 932 |
794 int notificationId = getNotificationId(downloadGuid); | 933 int notificationId = getNotificationId(downloadGuid); |
795 ChromeNotificationBuilder builder = | 934 ChromeNotificationBuilder builder = |
796 buildNotification(android.R.drawable.stat_sys_download_done, fil eName, | 935 buildNotification(android.R.drawable.stat_sys_download_done, fil eName, |
797 mContext.getResources().getString(R.string.download_noti fication_failed)); | 936 mContext.getResources().getString(R.string.download_noti fication_failed)); |
937 builder.setDeleteIntent(buildIconUpdateIntent(notificationId)); | |
798 updateNotification(notificationId, builder.build(), downloadGuid, isOffl inePage, null); | 938 updateNotification(notificationId, builder.build(), downloadGuid, isOffl inePage, null); |
799 stopTrackingInProgressDownload(downloadGuid, true); | 939 stopTrackingInProgressDownload(downloadGuid, true); |
800 } | 940 } |
801 | 941 |
802 /** | 942 /** |
803 * Helper method to build a PendingIntent from the provided intent. | 943 * Helper method to build a PendingIntent from the provided intent. |
804 * @param intent Intent to broadcast. | 944 * @param intent Intent to broadcast. |
805 * @param notificationId ID of the notification. | 945 * @param notificationId ID of the notification. |
806 */ | 946 */ |
807 private PendingIntent buildPendingIntent(Intent intent, int notificationId) { | 947 private PendingIntent buildPendingIntent(Intent intent, int notificationId) { |
808 return PendingIntent.getBroadcast( | 948 return PendingIntent.getBroadcast( |
809 mContext, notificationId, intent, PendingIntent.FLAG_UPDATE_CURR ENT); | 949 mContext, notificationId, intent, PendingIntent.FLAG_UPDATE_CURR ENT); |
810 } | 950 } |
811 | 951 |
812 /** | 952 /** |
953 * Helper method to build a {@link PendingIntent} that will wrap an {@link I ntent} that will | |
954 * update the summary notification icon. | |
955 * @param notificationId ID of the notification. | |
956 */ | |
957 // ---- BEFORE LANDING NOTE ---- | |
958 // TODO(dtrainor): This is not firing for the complete notification. Test o r remove this if we | |
959 // don't need it. | |
960 // ---- BEFORE LANDING NOTE ---- | |
961 private PendingIntent buildIconUpdateIntent(int notificationId) { | |
962 Intent intent = new Intent(mContext, DownloadBroadcastReceiver.class); | |
963 intent.setAction(ACTION_UPDATE_SUMMARY_ICON); | |
964 return buildPendingIntent(intent, notificationId); | |
965 } | |
966 | |
967 /** | |
813 * Helper method to build an download action Intent from the provided inform ation. | 968 * Helper method to build an download action Intent from the provided inform ation. |
814 * @param context {@link Context} to pull resources from. | 969 * @param context {@link Context} to pull resources from. |
815 * @param action Download action to perform. | 970 * @param action Download action to perform. |
816 * @param downloadGuid GUID of the download. | 971 * @param downloadGuid GUID of the download. |
817 * @param isOffTheRecord Whether the download is incognito. | 972 * @param isOffTheRecord Whether the download is incognito. |
818 * @param isOfflinePage Whether the download represents an Offline Page. | 973 * @param isOfflinePage Whether the download represents an Offline Page. |
819 */ | 974 */ |
820 static Intent buildActionIntent(Context context, String action, String downl oadGuid, | 975 static Intent buildActionIntent(Context context, String action, String downl oadGuid, |
821 boolean isOffTheRecord, boolean isOfflinePage) { | 976 boolean isOffTheRecord, boolean isOfflinePage) { |
822 ComponentName component = new ComponentName( | 977 ComponentName component = new ComponentName( |
823 context.getPackageName(), DownloadBroadcastReceiver.class.getNam e()); | 978 context.getPackageName(), DownloadBroadcastReceiver.class.getNam e()); |
824 Intent intent = new Intent(action); | 979 Intent intent = new Intent(action); |
825 intent.setComponent(component); | 980 intent.setComponent(component); |
826 intent.putExtra(EXTRA_DOWNLOAD_GUID, downloadGuid); | 981 intent.putExtra(EXTRA_DOWNLOAD_GUID, downloadGuid); |
827 intent.putExtra(EXTRA_IS_OFF_THE_RECORD, isOffTheRecord); | 982 intent.putExtra(EXTRA_IS_OFF_THE_RECORD, isOffTheRecord); |
828 intent.putExtra(EXTRA_IS_OFFLINE_PAGE, isOfflinePage); | 983 intent.putExtra(EXTRA_IS_OFFLINE_PAGE, isOfflinePage); |
829 return intent; | 984 return intent; |
830 } | 985 } |
831 | 986 |
832 /** | 987 /** |
833 * Builds a notification to be displayed. | 988 * Builds a notification to be displayed. |
834 * @param iconId Id of the notification icon. | 989 * @param iconId Id of the notification icon. |
835 * @param title Title of the notification. | 990 * @param title Title of the notification. |
836 * @param contentText Notification content text to be displayed. | 991 * @param contentText Notification content text to be displayed. |
837 * @return notification builder that builds the notification to be displayed | 992 * @return notification builder that builds the notification to be displayed |
838 */ | 993 */ |
839 private ChromeNotificationBuilder buildNotification( | 994 private ChromeNotificationBuilder buildNotification( |
840 int iconId, String title, String contentText) { | 995 int iconId, String title, String contentText) { |
996 Bundle extras = new Bundle(); | |
997 extras.putInt(EXTRA_NOTIFICATION_BUNDLE_ICON_ID, iconId); | |
998 | |
841 ChromeNotificationBuilder builder = | 999 ChromeNotificationBuilder builder = |
842 AppHooks.get() | 1000 AppHooks.get() |
843 .createChromeNotificationBuilder(true /* preferCompat */ , | 1001 .createChromeNotificationBuilder(true /* preferCompat */ , |
844 NotificationConstants.CATEGORY_ID_BROWSER, | 1002 NotificationConstants.CATEGORY_ID_BROWSER, |
845 mContext.getString(R.string.notification_categor y_browser), | 1003 mContext.getString(R.string.notification_categor y_browser), |
846 NotificationConstants.CATEGORY_GROUP_ID_GENERAL, | 1004 NotificationConstants.CATEGORY_GROUP_ID_GENERAL, |
847 mContext.getString(R.string.notification_categor y_group_general)) | 1005 mContext.getString(R.string.notification_categor y_group_general)) |
848 .setContentTitle( | 1006 .setContentTitle( |
849 DownloadUtils.getAbbreviatedFileName(title, MAX_ FILE_NAME_LENGTH)) | 1007 DownloadUtils.getAbbreviatedFileName(title, MAX_ FILE_NAME_LENGTH)) |
850 .setSmallIcon(iconId) | 1008 .setSmallIcon(iconId) |
851 .setLocalOnly(true) | 1009 .setLocalOnly(true) |
852 .setAutoCancel(true) | 1010 .setAutoCancel(true) |
853 .setContentText(contentText) | 1011 .setContentText(contentText) |
854 .setGroup(NotificationConstants.GROUP_DOWNLOADS); | 1012 .setGroup(NotificationConstants.GROUP_DOWNLOADS) |
1013 .addExtras(extras); | |
855 return builder; | 1014 return builder; |
856 } | 1015 } |
857 | 1016 |
858 private Bitmap getLargeNotificationIcon(Bitmap bitmap) { | 1017 private Bitmap getLargeNotificationIcon(Bitmap bitmap) { |
859 Resources resources = mContext.getResources(); | 1018 Resources resources = mContext.getResources(); |
860 int height = (int) resources.getDimension(android.R.dimen.notification_l arge_icon_height); | 1019 int height = (int) resources.getDimension(android.R.dimen.notification_l arge_icon_height); |
861 int width = (int) resources.getDimension(android.R.dimen.notification_la rge_icon_width); | 1020 int width = (int) resources.getDimension(android.R.dimen.notification_la rge_icon_width); |
862 final OvalShape circle = new OvalShape(); | 1021 final OvalShape circle = new OvalShape(); |
863 circle.resize(width, height); | 1022 circle.resize(width, height); |
864 final Paint paint = new Paint(); | 1023 final Paint paint = new Paint(); |
(...skipping 189 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1054 private void updateNotification(int id, Notification notification, String do wnloadGuid, | 1213 private void updateNotification(int id, Notification notification, String do wnloadGuid, |
1055 boolean isOfflinePage, DownloadSharedPreferenceEntry entry) { | 1214 boolean isOfflinePage, DownloadSharedPreferenceEntry entry) { |
1056 updateNotification(id, notification); | 1215 updateNotification(id, notification); |
1057 trackNotificationUma(isOfflinePage, downloadGuid); | 1216 trackNotificationUma(isOfflinePage, downloadGuid); |
1058 | 1217 |
1059 if (entry != null) { | 1218 if (entry != null) { |
1060 mDownloadSharedPreferenceHelper.addOrReplaceSharedPreferenceEntry(en try); | 1219 mDownloadSharedPreferenceHelper.addOrReplaceSharedPreferenceEntry(en try); |
1061 } else { | 1220 } else { |
1062 mDownloadSharedPreferenceHelper.removeSharedPreferenceEntry(download Guid); | 1221 mDownloadSharedPreferenceHelper.removeSharedPreferenceEntry(download Guid); |
1063 } | 1222 } |
1223 updateSummaryIcon(-1, new Pair<Integer, Notification>(id, notification)) ; | |
1064 } | 1224 } |
1065 | 1225 |
1066 private void trackNotificationUma(boolean isOfflinePage, String downloadGuid ) { | 1226 private void trackNotificationUma(boolean isOfflinePage, String downloadGuid ) { |
1067 // Check if we already have an entry in the DownloadSharedPreferenceHelp er. This is a | 1227 // Check if we already have an entry in the DownloadSharedPreferenceHelp er. This is a |
1068 // reasonable indicator for whether or not a notification is already sho wing (or at least if | 1228 // reasonable indicator for whether or not a notification is already sho wing (or at least if |
1069 // we had built one for this download before. | 1229 // we had built one for this download before. |
1070 if (mDownloadSharedPreferenceHelper.hasEntry(downloadGuid)) return; | 1230 if (mDownloadSharedPreferenceHelper.hasEntry(downloadGuid)) return; |
1071 NotificationUmaTracker.getInstance().onNotificationShown(isOfflinePage | 1231 NotificationUmaTracker.getInstance().onNotificationShown(isOfflinePage |
1072 ? NotificationUmaTracker.DOWNLOAD_PAGES | 1232 ? NotificationUmaTracker.DOWNLOAD_PAGES |
1073 : NotificationUmaTracker.DOWNLOAD_FILES); | 1233 : NotificationUmaTracker.DOWNLOAD_FILES); |
(...skipping 109 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1183 return context.getString(R.string.remaining_duration_minutes, minute s); | 1343 return context.getString(R.string.remaining_duration_minutes, minute s); |
1184 } else if (minutes > 0) { | 1344 } else if (minutes > 0) { |
1185 return context.getString(R.string.remaining_duration_one_minute); | 1345 return context.getString(R.string.remaining_duration_one_minute); |
1186 } else if (seconds == 1) { | 1346 } else if (seconds == 1) { |
1187 return context.getString(R.string.remaining_duration_one_second); | 1347 return context.getString(R.string.remaining_duration_one_second); |
1188 } else { | 1348 } else { |
1189 return context.getString(R.string.remaining_duration_seconds, second s); | 1349 return context.getString(R.string.remaining_duration_seconds, second s); |
1190 } | 1350 } |
1191 } | 1351 } |
1192 } | 1352 } |
OLD | NEW |