1a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey/* 2a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey * Copyright (C) 2012 The Android Open Source Project 3a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey * 4a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey * Licensed under the Apache License, Version 2.0 (the "License"); 5a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey * you may not use this file except in compliance with the License. 6a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey * You may obtain a copy of the License at 7a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey * 8a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey * http://www.apache.org/licenses/LICENSE-2.0 9a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey * 10a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey * Unless required by applicable law or agreed to in writing, software 11a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey * distributed under the License is distributed on an "AS IS" BASIS, 12a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey * See the License for the specific language governing permissions and 14a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey * limitations under the License. 15a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey */ 16a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey 17a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkeypackage com.android.providers.downloads; 18a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey 19a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkeyimport static android.app.DownloadManager.Request.VISIBILITY_VISIBLE; 20a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkeyimport static android.app.DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED; 21a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkeyimport static android.app.DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_ONLY_COMPLETION; 222a34a9359dc199e98a45275a22027b5c367933a6Jeff Sharkeyimport static android.provider.Downloads.Impl.STATUS_QUEUED_FOR_WIFI; 231ad10ce731d1b54692d7d5ee32601e965f503fa4Jeff Sharkeyimport static android.provider.Downloads.Impl.STATUS_RUNNING; 243a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey 25703bc3a83056a878a83e263b992fb5331b84535fJeff Sharkeyimport static com.android.providers.downloads.Constants.TAG; 26a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey 27a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkeyimport android.app.DownloadManager; 28a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkeyimport android.app.Notification; 299b2f2a5f041bf56ab45e5952ae4ddc4b47d0105eJeff Sharkeyimport android.app.NotificationChannel; 30a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkeyimport android.app.NotificationManager; 31a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkeyimport android.app.PendingIntent; 32a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkeyimport android.content.ContentUris; 33a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkeyimport android.content.Context; 34a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkeyimport android.content.Intent; 35a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkeyimport android.content.res.Resources; 363a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkeyimport android.database.Cursor; 37a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkeyimport android.net.Uri; 38703bc3a83056a878a83e263b992fb5331b84535fJeff Sharkeyimport android.os.SystemClock; 39a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkeyimport android.provider.Downloads; 4008fa91badf0b34323c535dde6f8113e16ae94e21Jeff Sharkeyimport android.service.notification.StatusBarNotification; 41a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkeyimport android.text.TextUtils; 4252b703c5d0c4cff72bafdec0e2229368d3cc20d0Jeff Sharkeyimport android.text.format.DateUtils; 433a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkeyimport android.util.ArrayMap; 443a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkeyimport android.util.IntArray; 45703bc3a83056a878a83e263b992fb5331b84535fJeff Sharkeyimport android.util.Log; 4638648831a92295e9a11831e19e5a9dab4cbd939eJeff Sharkeyimport android.util.LongSparseLongArray; 47a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey 4808fa91badf0b34323c535dde6f8113e16ae94e21Jeff Sharkeyimport com.android.internal.util.ArrayUtils; 4908fa91badf0b34323c535dde6f8113e16ae94e21Jeff Sharkey 50250a1ebc4cf52edf4e55a594f05f4a351ee8e126Elliott Hughesimport java.text.NumberFormat; 51a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey 52a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkeyimport javax.annotation.concurrent.GuardedBy; 53a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey 54a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey/** 553a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey * Update {@link NotificationManager} to reflect current download states. 563a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey * Collapses similar downloads into a single notification, and builds 57a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey * {@link PendingIntent} that launch towards {@link DownloadReceiver}. 58a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey */ 59a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkeypublic class DownloadNotifier { 60a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey 61a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey private static final int TYPE_ACTIVE = 1; 62a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey private static final int TYPE_WAITING = 2; 63a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey private static final int TYPE_COMPLETE = 3; 64a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey 659b2f2a5f041bf56ab45e5952ae4ddc4b47d0105eJeff Sharkey private static final String CHANNEL_ACTIVE = "active"; 669b2f2a5f041bf56ab45e5952ae4ddc4b47d0105eJeff Sharkey private static final String CHANNEL_WAITING = "waiting"; 679b2f2a5f041bf56ab45e5952ae4ddc4b47d0105eJeff Sharkey private static final String CHANNEL_COMPLETE = "complete"; 689b2f2a5f041bf56ab45e5952ae4ddc4b47d0105eJeff Sharkey 69a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey private final Context mContext; 70a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey private final NotificationManager mNotifManager; 71a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey 72a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey /** 73a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey * Currently active notifications, mapped from clustering tag to timestamp 74a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey * when first shown. 75a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey * 763a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey * @see #buildNotificationTag(Cursor) 77a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey */ 78a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey @GuardedBy("mActiveNotifs") 793a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey private final ArrayMap<String, Long> mActiveNotifs = new ArrayMap<>(); 80a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey 8138648831a92295e9a11831e19e5a9dab4cbd939eJeff Sharkey /** 823a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey * Current speed of active downloads, mapped from download ID to speed in 833a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey * bytes per second. 8438648831a92295e9a11831e19e5a9dab4cbd939eJeff Sharkey */ 8538648831a92295e9a11831e19e5a9dab4cbd939eJeff Sharkey @GuardedBy("mDownloadSpeed") 8638648831a92295e9a11831e19e5a9dab4cbd939eJeff Sharkey private final LongSparseLongArray mDownloadSpeed = new LongSparseLongArray(); 8738648831a92295e9a11831e19e5a9dab4cbd939eJeff Sharkey 88703bc3a83056a878a83e263b992fb5331b84535fJeff Sharkey /** 893a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey * Last time speed was reproted, mapped from download ID to 90703bc3a83056a878a83e263b992fb5331b84535fJeff Sharkey * {@link SystemClock#elapsedRealtime()}. 91703bc3a83056a878a83e263b992fb5331b84535fJeff Sharkey */ 92703bc3a83056a878a83e263b992fb5331b84535fJeff Sharkey @GuardedBy("mDownloadSpeed") 93703bc3a83056a878a83e263b992fb5331b84535fJeff Sharkey private final LongSparseLongArray mDownloadTouch = new LongSparseLongArray(); 94703bc3a83056a878a83e263b992fb5331b84535fJeff Sharkey 95a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey public DownloadNotifier(Context context) { 96a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey mContext = context; 979b2f2a5f041bf56ab45e5952ae4ddc4b47d0105eJeff Sharkey mNotifManager = context.getSystemService(NotificationManager.class); 989b2f2a5f041bf56ab45e5952ae4ddc4b47d0105eJeff Sharkey 999b2f2a5f041bf56ab45e5952ae4ddc4b47d0105eJeff Sharkey // Ensure that all our channels are ready to use 1009b2f2a5f041bf56ab45e5952ae4ddc4b47d0105eJeff Sharkey mNotifManager.createNotificationChannel(new NotificationChannel(CHANNEL_ACTIVE, 1019b2f2a5f041bf56ab45e5952ae4ddc4b47d0105eJeff Sharkey context.getText(R.string.download_running), 102b0a086c29f9fe63ced4524b899c2ca2040fce328Geoffrey Pitsch NotificationManager.IMPORTANCE_MIN)); 1039b2f2a5f041bf56ab45e5952ae4ddc4b47d0105eJeff Sharkey mNotifManager.createNotificationChannel(new NotificationChannel(CHANNEL_WAITING, 1049b2f2a5f041bf56ab45e5952ae4ddc4b47d0105eJeff Sharkey context.getText(R.string.download_queued), 1059b2f2a5f041bf56ab45e5952ae4ddc4b47d0105eJeff Sharkey NotificationManager.IMPORTANCE_DEFAULT)); 1069b2f2a5f041bf56ab45e5952ae4ddc4b47d0105eJeff Sharkey mNotifManager.createNotificationChannel(new NotificationChannel(CHANNEL_COMPLETE, 1079b2f2a5f041bf56ab45e5952ae4ddc4b47d0105eJeff Sharkey context.getText(com.android.internal.R.string.done_label), 1089b2f2a5f041bf56ab45e5952ae4ddc4b47d0105eJeff Sharkey NotificationManager.IMPORTANCE_DEFAULT)); 109a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey } 110a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey 11108fa91badf0b34323c535dde6f8113e16ae94e21Jeff Sharkey public void init() { 11208fa91badf0b34323c535dde6f8113e16ae94e21Jeff Sharkey synchronized (mActiveNotifs) { 11308fa91badf0b34323c535dde6f8113e16ae94e21Jeff Sharkey mActiveNotifs.clear(); 11408fa91badf0b34323c535dde6f8113e16ae94e21Jeff Sharkey final StatusBarNotification[] notifs = mNotifManager.getActiveNotifications(); 11508fa91badf0b34323c535dde6f8113e16ae94e21Jeff Sharkey if (!ArrayUtils.isEmpty(notifs)) { 11608fa91badf0b34323c535dde6f8113e16ae94e21Jeff Sharkey for (StatusBarNotification notif : notifs) { 11708fa91badf0b34323c535dde6f8113e16ae94e21Jeff Sharkey mActiveNotifs.put(notif.getTag(), notif.getPostTime()); 11808fa91badf0b34323c535dde6f8113e16ae94e21Jeff Sharkey } 11908fa91badf0b34323c535dde6f8113e16ae94e21Jeff Sharkey } 12008fa91badf0b34323c535dde6f8113e16ae94e21Jeff Sharkey } 121b0bb182a1b52b31ccf1ec5e0be82308ebb4857e2Jeff Sharkey } 122b0bb182a1b52b31ccf1ec5e0be82308ebb4857e2Jeff Sharkey 123a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey /** 124703bc3a83056a878a83e263b992fb5331b84535fJeff Sharkey * Notify the current speed of an active download, used for calculating 12538648831a92295e9a11831e19e5a9dab4cbd939eJeff Sharkey * estimated remaining time. 12638648831a92295e9a11831e19e5a9dab4cbd939eJeff Sharkey */ 12738648831a92295e9a11831e19e5a9dab4cbd939eJeff Sharkey public void notifyDownloadSpeed(long id, long bytesPerSecond) { 12838648831a92295e9a11831e19e5a9dab4cbd939eJeff Sharkey synchronized (mDownloadSpeed) { 12938648831a92295e9a11831e19e5a9dab4cbd939eJeff Sharkey if (bytesPerSecond != 0) { 13038648831a92295e9a11831e19e5a9dab4cbd939eJeff Sharkey mDownloadSpeed.put(id, bytesPerSecond); 131703bc3a83056a878a83e263b992fb5331b84535fJeff Sharkey mDownloadTouch.put(id, SystemClock.elapsedRealtime()); 13238648831a92295e9a11831e19e5a9dab4cbd939eJeff Sharkey } else { 13338648831a92295e9a11831e19e5a9dab4cbd939eJeff Sharkey mDownloadSpeed.delete(id); 134703bc3a83056a878a83e263b992fb5331b84535fJeff Sharkey mDownloadTouch.delete(id); 13538648831a92295e9a11831e19e5a9dab4cbd939eJeff Sharkey } 13638648831a92295e9a11831e19e5a9dab4cbd939eJeff Sharkey } 13738648831a92295e9a11831e19e5a9dab4cbd939eJeff Sharkey } 13838648831a92295e9a11831e19e5a9dab4cbd939eJeff Sharkey 1393a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey private interface UpdateQuery { 1403a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey final String[] PROJECTION = new String[] { 1413a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey Downloads.Impl._ID, 1423a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey Downloads.Impl.COLUMN_STATUS, 1433a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey Downloads.Impl.COLUMN_VISIBILITY, 1443a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey Downloads.Impl.COLUMN_NOTIFICATION_PACKAGE, 1453a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey Downloads.Impl.COLUMN_CURRENT_BYTES, 1463a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey Downloads.Impl.COLUMN_TOTAL_BYTES, 1473a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey Downloads.Impl.COLUMN_DESTINATION, 1483a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey Downloads.Impl.COLUMN_TITLE, 1493a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey Downloads.Impl.COLUMN_DESCRIPTION, 1503a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey }; 1513a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey 1523a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey final int _ID = 0; 1533a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey final int STATUS = 1; 1543a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey final int VISIBILITY = 2; 1553a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey final int NOTIFICATION_PACKAGE = 3; 1563a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey final int CURRENT_BYTES = 4; 1573a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey final int TOTAL_BYTES = 5; 1583a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey final int DESTINATION = 6; 1593a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey final int TITLE = 7; 1603a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey final int DESCRIPTION = 8; 161a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey } 162a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey 1633a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey public void update() { 1643a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey try (Cursor cursor = mContext.getContentResolver().query( 1653a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey Downloads.Impl.ALL_DOWNLOADS_CONTENT_URI, UpdateQuery.PROJECTION, 1663a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey Downloads.Impl.COLUMN_DELETED + " == '0'", null, null)) { 1673a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey synchronized (mActiveNotifs) { 1683a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey updateWithLocked(cursor); 1693a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey } 1706971133998ddc8c8c6b37b2fdaaec1d3ed152e90Oren Blasberg } 1716971133998ddc8c8c6b37b2fdaaec1d3ed152e90Oren Blasberg } 1726971133998ddc8c8c6b37b2fdaaec1d3ed152e90Oren Blasberg 1733a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey private void updateWithLocked(Cursor cursor) { 174a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey final Resources res = mContext.getResources(); 175a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey 176a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey // Cluster downloads together 1773a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey final ArrayMap<String, IntArray> clustered = new ArrayMap<>(); 1783a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey while (cursor.moveToNext()) { 1793a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey final String tag = buildNotificationTag(cursor); 180a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey if (tag != null) { 1813a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey IntArray cluster = clustered.get(tag); 1823a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey if (cluster == null) { 1833a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey cluster = new IntArray(); 1843a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey clustered.put(tag, cluster); 1853a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey } 1863a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey cluster.add(cursor.getPosition()); 187a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey } 188a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey } 189a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey 190a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey // Build notification for each cluster 1913a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey for (int i = 0; i < clustered.size(); i++) { 1923a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey final String tag = clustered.keyAt(i); 1933a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey final IntArray cluster = clustered.valueAt(i); 194a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey final int type = getNotificationTagType(tag); 1956971133998ddc8c8c6b37b2fdaaec1d3ed152e90Oren Blasberg 1969b2f2a5f041bf56ab45e5952ae4ddc4b47d0105eJeff Sharkey final Notification.Builder builder; 1979b2f2a5f041bf56ab45e5952ae4ddc4b47d0105eJeff Sharkey if (type == TYPE_ACTIVE) { 1989b2f2a5f041bf56ab45e5952ae4ddc4b47d0105eJeff Sharkey builder = new Notification.Builder(mContext, CHANNEL_ACTIVE); 1999b2f2a5f041bf56ab45e5952ae4ddc4b47d0105eJeff Sharkey builder.setSmallIcon(android.R.drawable.stat_sys_download); 2009b2f2a5f041bf56ab45e5952ae4ddc4b47d0105eJeff Sharkey } else if (type == TYPE_WAITING) { 2019b2f2a5f041bf56ab45e5952ae4ddc4b47d0105eJeff Sharkey builder = new Notification.Builder(mContext, CHANNEL_WAITING); 2029b2f2a5f041bf56ab45e5952ae4ddc4b47d0105eJeff Sharkey builder.setSmallIcon(android.R.drawable.stat_sys_warning); 2039b2f2a5f041bf56ab45e5952ae4ddc4b47d0105eJeff Sharkey } else if (type == TYPE_COMPLETE) { 2049b2f2a5f041bf56ab45e5952ae4ddc4b47d0105eJeff Sharkey builder = new Notification.Builder(mContext, CHANNEL_COMPLETE); 2059b2f2a5f041bf56ab45e5952ae4ddc4b47d0105eJeff Sharkey builder.setSmallIcon(android.R.drawable.stat_sys_download_done); 2069b2f2a5f041bf56ab45e5952ae4ddc4b47d0105eJeff Sharkey } else { 2079b2f2a5f041bf56ab45e5952ae4ddc4b47d0105eJeff Sharkey continue; 2089b2f2a5f041bf56ab45e5952ae4ddc4b47d0105eJeff Sharkey } 2099b2f2a5f041bf56ab45e5952ae4ddc4b47d0105eJeff Sharkey 21052d35ed20edec2b5086459f1d5d64309c421f3b2Selim Cinek builder.setColor(res.getColor( 21152d35ed20edec2b5086459f1d5d64309c421f3b2Selim Cinek com.android.internal.R.color.system_notification_accent_color)); 212a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey 213a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey // Use time when cluster was first shown to avoid shuffling 214a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey final long firstShown; 215a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey if (mActiveNotifs.containsKey(tag)) { 216a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey firstShown = mActiveNotifs.get(tag); 217a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey } else { 218a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey firstShown = System.currentTimeMillis(); 219a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey mActiveNotifs.put(tag, firstShown); 220a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey } 221a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey builder.setWhen(firstShown); 2224b858ed02b773344cfedd66e516c6d365da55974Jeff Sharkey builder.setOnlyAlertOnce(true); 223a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey 224a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey // Build action intents 225a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey if (type == TYPE_ACTIVE || type == TYPE_WAITING) { 2263a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey final long[] downloadIds = getDownloadIds(cursor, cluster); 2276971133998ddc8c8c6b37b2fdaaec1d3ed152e90Oren Blasberg 228af49093b1554fdc55977e9281505e05865f33813Danny Baumann // build a synthetic uri for intent identification purposes 229af49093b1554fdc55977e9281505e05865f33813Danny Baumann final Uri uri = new Uri.Builder().scheme("active-dl").appendPath(tag).build(); 230a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey final Intent intent = new Intent(Constants.ACTION_LIST, 231af49093b1554fdc55977e9281505e05865f33813Danny Baumann uri, mContext, DownloadReceiver.class); 232ae89e55bb059b37922a3745b1b45ac79a71a05beJeff Sharkey intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND); 233a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey intent.putExtra(DownloadManager.EXTRA_NOTIFICATION_CLICK_DOWNLOAD_IDS, 2346971133998ddc8c8c6b37b2fdaaec1d3ed152e90Oren Blasberg downloadIds); 235af49093b1554fdc55977e9281505e05865f33813Danny Baumann builder.setContentIntent(PendingIntent.getBroadcast(mContext, 236af49093b1554fdc55977e9281505e05865f33813Danny Baumann 0, intent, PendingIntent.FLAG_UPDATE_CURRENT)); 2377b7fa39decefca0ff83b1e3758cba11df90f94cdJeff Sharkey if (type == TYPE_ACTIVE) { 2387b7fa39decefca0ff83b1e3758cba11df90f94cdJeff Sharkey builder.setOngoing(true); 2397b7fa39decefca0ff83b1e3758cba11df90f94cdJeff Sharkey } 240a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey 2416971133998ddc8c8c6b37b2fdaaec1d3ed152e90Oren Blasberg // Add a Cancel action 2426971133998ddc8c8c6b37b2fdaaec1d3ed152e90Oren Blasberg final Uri cancelUri = new Uri.Builder().scheme("cancel-dl").appendPath(tag).build(); 2436971133998ddc8c8c6b37b2fdaaec1d3ed152e90Oren Blasberg final Intent cancelIntent = new Intent(Constants.ACTION_CANCEL, 2446971133998ddc8c8c6b37b2fdaaec1d3ed152e90Oren Blasberg cancelUri, mContext, DownloadReceiver.class); 245ae89e55bb059b37922a3745b1b45ac79a71a05beJeff Sharkey cancelIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND); 2466971133998ddc8c8c6b37b2fdaaec1d3ed152e90Oren Blasberg cancelIntent.putExtra(DownloadReceiver.EXTRA_CANCELED_DOWNLOAD_IDS, downloadIds); 2476971133998ddc8c8c6b37b2fdaaec1d3ed152e90Oren Blasberg cancelIntent.putExtra(DownloadReceiver.EXTRA_CANCELED_DOWNLOAD_NOTIFICATION_TAG, tag); 2486971133998ddc8c8c6b37b2fdaaec1d3ed152e90Oren Blasberg 2496971133998ddc8c8c6b37b2fdaaec1d3ed152e90Oren Blasberg builder.addAction( 2506971133998ddc8c8c6b37b2fdaaec1d3ed152e90Oren Blasberg android.R.drawable.ic_menu_close_clear_cancel, 2516971133998ddc8c8c6b37b2fdaaec1d3ed152e90Oren Blasberg res.getString(R.string.button_cancel_download), 2526971133998ddc8c8c6b37b2fdaaec1d3ed152e90Oren Blasberg PendingIntent.getBroadcast(mContext, 2536971133998ddc8c8c6b37b2fdaaec1d3ed152e90Oren Blasberg 0, cancelIntent, PendingIntent.FLAG_UPDATE_CURRENT)); 2546971133998ddc8c8c6b37b2fdaaec1d3ed152e90Oren Blasberg 255a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey } else if (type == TYPE_COMPLETE) { 2563a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey cursor.moveToPosition(cluster.get(0)); 2573a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey final long id = cursor.getLong(UpdateQuery._ID); 2583a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey final int status = cursor.getInt(UpdateQuery.STATUS); 2593a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey final int destination = cursor.getInt(UpdateQuery.DESTINATION); 2603a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey 261a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey final Uri uri = ContentUris.withAppendedId( 2623a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey Downloads.Impl.ALL_DOWNLOADS_CONTENT_URI, id); 26364a9e6cb3628db724e04492cf207757348960eb7Jeff Sharkey builder.setAutoCancel(true); 264a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey 265a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey final String action; 2663a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey if (Downloads.Impl.isStatusError(status)) { 267a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey action = Constants.ACTION_LIST; 268a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey } else { 269d635ac295850ba23d528c02b1e5c6eb44b64b22bJeff Sharkey action = Constants.ACTION_OPEN; 270a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey } 271a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey 272a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey final Intent intent = new Intent(action, uri, mContext, DownloadReceiver.class); 273ae89e55bb059b37922a3745b1b45ac79a71a05beJeff Sharkey intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND); 274a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey intent.putExtra(DownloadManager.EXTRA_NOTIFICATION_CLICK_DOWNLOAD_IDS, 2753a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey getDownloadIds(cursor, cluster)); 276af49093b1554fdc55977e9281505e05865f33813Danny Baumann builder.setContentIntent(PendingIntent.getBroadcast(mContext, 277af49093b1554fdc55977e9281505e05865f33813Danny Baumann 0, intent, PendingIntent.FLAG_UPDATE_CURRENT)); 278a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey 279a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey final Intent hideIntent = new Intent(Constants.ACTION_HIDE, 280a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey uri, mContext, DownloadReceiver.class); 281ae89e55bb059b37922a3745b1b45ac79a71a05beJeff Sharkey hideIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND); 282a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey builder.setDeleteIntent(PendingIntent.getBroadcast(mContext, 0, hideIntent, 0)); 283a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey } 284a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey 285a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey // Calculate and show progress 286a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey String remainingText = null; 287a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey String percentText = null; 288a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey if (type == TYPE_ACTIVE) { 289a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey long current = 0; 290a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey long total = 0; 291fec5f50a85e1bfc7bb4fa12d04ffa7526c79fad7Jeff Sharkey long speed = 0; 29238648831a92295e9a11831e19e5a9dab4cbd939eJeff Sharkey synchronized (mDownloadSpeed) { 2933a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey for (int j = 0; j < cluster.size(); j++) { 2943a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey cursor.moveToPosition(cluster.get(j)); 2953a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey 2963a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey final long id = cursor.getLong(UpdateQuery._ID); 2973a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey final long currentBytes = cursor.getLong(UpdateQuery.CURRENT_BYTES); 2983a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey final long totalBytes = cursor.getLong(UpdateQuery.TOTAL_BYTES); 2993a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey 3003a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey if (totalBytes != -1) { 3013a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey current += currentBytes; 3023a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey total += totalBytes; 3033a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey speed += mDownloadSpeed.get(id); 30438648831a92295e9a11831e19e5a9dab4cbd939eJeff Sharkey } 305a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey } 306a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey } 307a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey 308a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey if (total > 0) { 309250a1ebc4cf52edf4e55a594f05f4a351ee8e126Elliott Hughes percentText = 310250a1ebc4cf52edf4e55a594f05f4a351ee8e126Elliott Hughes NumberFormat.getPercentInstance().format((double) current / total); 311fec5f50a85e1bfc7bb4fa12d04ffa7526c79fad7Jeff Sharkey 312fec5f50a85e1bfc7bb4fa12d04ffa7526c79fad7Jeff Sharkey if (speed > 0) { 313fec5f50a85e1bfc7bb4fa12d04ffa7526c79fad7Jeff Sharkey final long remainingMillis = ((total - current) * 1000) / speed; 31452b703c5d0c4cff72bafdec0e2229368d3cc20d0Jeff Sharkey remainingText = res.getString(R.string.download_remaining, 31552b703c5d0c4cff72bafdec0e2229368d3cc20d0Jeff Sharkey DateUtils.formatDuration(remainingMillis)); 31652b703c5d0c4cff72bafdec0e2229368d3cc20d0Jeff Sharkey } 317a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey 318250a1ebc4cf52edf4e55a594f05f4a351ee8e126Elliott Hughes final int percent = (int) ((current * 100) / total); 319a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey builder.setProgress(100, percent, false); 320a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey } else { 321a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey builder.setProgress(100, 0, true); 322a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey } 323a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey } 324a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey 325a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey // Build titles and description 326a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey final Notification notif; 327a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey if (cluster.size() == 1) { 3283a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey cursor.moveToPosition(cluster.get(0)); 3293a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey builder.setContentTitle(getDownloadTitle(res, cursor)); 330a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey 331a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey if (type == TYPE_ACTIVE) { 3323a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey final String description = cursor.getString(UpdateQuery.DESCRIPTION); 3333a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey if (!TextUtils.isEmpty(description)) { 3343a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey builder.setContentText(description); 335a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey } else { 336a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey builder.setContentText(remainingText); 337a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey } 338a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey builder.setContentInfo(percentText); 339a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey 340a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey } else if (type == TYPE_WAITING) { 341a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey builder.setContentText( 342a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey res.getString(R.string.notification_need_wifi_for_size)); 343a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey 344a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey } else if (type == TYPE_COMPLETE) { 3453a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey final int status = cursor.getInt(UpdateQuery.STATUS); 3463a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey if (Downloads.Impl.isStatusError(status)) { 347a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey builder.setContentText(res.getText(R.string.notification_download_failed)); 3483a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey } else if (Downloads.Impl.isStatusSuccess(status)) { 349a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey builder.setContentText( 350a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey res.getText(R.string.notification_download_complete)); 351a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey } 352a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey } 353a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey 354a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey notif = builder.build(); 355a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey 356a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey } else { 357a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey final Notification.InboxStyle inboxStyle = new Notification.InboxStyle(builder); 358a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey 3593a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey for (int j = 0; j < cluster.size(); j++) { 3603a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey cursor.moveToPosition(cluster.get(j)); 3613a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey inboxStyle.addLine(getDownloadTitle(res, cursor)); 362a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey } 363a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey 364a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey if (type == TYPE_ACTIVE) { 365a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey builder.setContentTitle(res.getQuantityString( 366a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey R.plurals.notif_summary_active, cluster.size(), cluster.size())); 367a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey builder.setContentText(remainingText); 368a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey builder.setContentInfo(percentText); 369a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey inboxStyle.setSummaryText(remainingText); 370a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey 371a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey } else if (type == TYPE_WAITING) { 372a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey builder.setContentTitle(res.getQuantityString( 373a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey R.plurals.notif_summary_waiting, cluster.size(), cluster.size())); 374a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey builder.setContentText( 375a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey res.getString(R.string.notification_need_wifi_for_size)); 376a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey inboxStyle.setSummaryText( 377a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey res.getString(R.string.notification_need_wifi_for_size)); 378a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey } 379a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey 380a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey notif = inboxStyle.build(); 381a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey } 382a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey 383a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey mNotifManager.notify(tag, 0, notif); 384a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey } 385a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey 386a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey // Remove stale tags that weren't renewed 3873a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey for (int i = 0; i < mActiveNotifs.size();) { 3883a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey final String tag = mActiveNotifs.keyAt(i); 3893a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey if (clustered.containsKey(tag)) { 3903a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey i++; 3913a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey } else { 392a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey mNotifManager.cancel(tag, 0); 3933a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey mActiveNotifs.removeAt(i); 394a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey } 395a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey } 396a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey } 397a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey 3983a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey private static CharSequence getDownloadTitle(Resources res, Cursor cursor) { 3993a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey final String title = cursor.getString(UpdateQuery.TITLE); 4003a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey if (!TextUtils.isEmpty(title)) { 4013a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey return title; 402a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey } else { 403a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey return res.getString(R.string.download_unknown_title); 404a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey } 405a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey } 406a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey 4073a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey private long[] getDownloadIds(Cursor cursor, IntArray cluster) { 4083a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey final long[] ids = new long[cluster.size()]; 4093a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey for (int i = 0; i < cluster.size(); i++) { 4103a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey cursor.moveToPosition(cluster.get(i)); 4113a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey ids[i] = cursor.getLong(UpdateQuery._ID); 412a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey } 413a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey return ids; 414a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey } 415a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey 416703bc3a83056a878a83e263b992fb5331b84535fJeff Sharkey public void dumpSpeeds() { 417703bc3a83056a878a83e263b992fb5331b84535fJeff Sharkey synchronized (mDownloadSpeed) { 418703bc3a83056a878a83e263b992fb5331b84535fJeff Sharkey for (int i = 0; i < mDownloadSpeed.size(); i++) { 419703bc3a83056a878a83e263b992fb5331b84535fJeff Sharkey final long id = mDownloadSpeed.keyAt(i); 420703bc3a83056a878a83e263b992fb5331b84535fJeff Sharkey final long delta = SystemClock.elapsedRealtime() - mDownloadTouch.get(id); 421703bc3a83056a878a83e263b992fb5331b84535fJeff Sharkey Log.d(TAG, "Download " + id + " speed " + mDownloadSpeed.valueAt(i) + "bps, " 422703bc3a83056a878a83e263b992fb5331b84535fJeff Sharkey + delta + "ms ago"); 423703bc3a83056a878a83e263b992fb5331b84535fJeff Sharkey } 424703bc3a83056a878a83e263b992fb5331b84535fJeff Sharkey } 425703bc3a83056a878a83e263b992fb5331b84535fJeff Sharkey } 426703bc3a83056a878a83e263b992fb5331b84535fJeff Sharkey 427a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey /** 4283a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey * Build tag used for collapsing several downloads into a single 429a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey * {@link Notification}. 430a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey */ 4313a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey private static String buildNotificationTag(Cursor cursor) { 4323a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey final long id = cursor.getLong(UpdateQuery._ID); 4333a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey final int status = cursor.getInt(UpdateQuery.STATUS); 4343a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey final int visibility = cursor.getInt(UpdateQuery.VISIBILITY); 4353a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey final String notifPackage = cursor.getString(UpdateQuery.NOTIFICATION_PACKAGE); 4363a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey 4372a34a9359dc199e98a45275a22027b5c367933a6Jeff Sharkey if (isQueuedAndVisible(status, visibility)) { 4383a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey return TYPE_WAITING + ":" + notifPackage; 4393a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey } else if (isActiveAndVisible(status, visibility)) { 4403a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey return TYPE_ACTIVE + ":" + notifPackage; 4413a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey } else if (isCompleteAndVisible(status, visibility)) { 442a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey // Complete downloads always have unique notifs 4433a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey return TYPE_COMPLETE + ":" + id; 444a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey } else { 445a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey return null; 446a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey } 447a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey } 448a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey 449a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey /** 450a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey * Return the cluster type of the given tag, as created by 4513a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey * {@link #buildNotificationTag(Cursor)}. 452a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey */ 453a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey private static int getNotificationTagType(String tag) { 454a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey return Integer.parseInt(tag.substring(0, tag.indexOf(':'))); 455a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey } 456a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey 4572a34a9359dc199e98a45275a22027b5c367933a6Jeff Sharkey private static boolean isQueuedAndVisible(int status, int visibility) { 4582a34a9359dc199e98a45275a22027b5c367933a6Jeff Sharkey return status == STATUS_QUEUED_FOR_WIFI && 4592a34a9359dc199e98a45275a22027b5c367933a6Jeff Sharkey (visibility == VISIBILITY_VISIBLE 4602a34a9359dc199e98a45275a22027b5c367933a6Jeff Sharkey || visibility == VISIBILITY_VISIBLE_NOTIFY_COMPLETED); 4612a34a9359dc199e98a45275a22027b5c367933a6Jeff Sharkey } 4622a34a9359dc199e98a45275a22027b5c367933a6Jeff Sharkey 4633a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey private static boolean isActiveAndVisible(int status, int visibility) { 4643a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey return status == STATUS_RUNNING && 4653a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey (visibility == VISIBILITY_VISIBLE 4663a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey || visibility == VISIBILITY_VISIBLE_NOTIFY_COMPLETED); 467a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey } 468a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey 4693a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey private static boolean isCompleteAndVisible(int status, int visibility) { 4703a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey return Downloads.Impl.isStatusCompleted(status) && 4713a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey (visibility == VISIBILITY_VISIBLE_NOTIFY_COMPLETED 4723a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey || visibility == VISIBILITY_VISIBLE_NOTIFY_ONLY_COMPLETION); 473a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey } 474a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey} 475