DownloadNotifier.java revision a40a349c0107660bfb4004467550725a3ca3ec97
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; 22a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey 23a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkeyimport android.app.DownloadManager; 24a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkeyimport android.app.Notification; 25a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkeyimport android.app.NotificationManager; 26a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkeyimport android.app.PendingIntent; 27a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkeyimport android.content.ContentUris; 28a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkeyimport android.content.Context; 29a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkeyimport android.content.Intent; 30a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkeyimport android.content.res.Resources; 31a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkeyimport android.net.Uri; 32a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkeyimport android.provider.Downloads; 33a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkeyimport android.text.TextUtils; 34a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey 35a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkeyimport com.google.common.collect.ArrayListMultimap; 36a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkeyimport com.google.common.collect.Maps; 37a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkeyimport com.google.common.collect.Multimap; 38a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkeyimport com.google.common.collect.Sets; 39a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey 40a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkeyimport java.util.Collection; 41a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkeyimport java.util.HashMap; 42a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkeyimport java.util.Iterator; 43a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkeyimport java.util.Set; 44a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey 45a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkeyimport javax.annotation.concurrent.GuardedBy; 46a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey 47a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey/** 48a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey * Update {@link NotificationManager} to reflect current {@link DownloadInfo} 49a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey * states. Collapses similar downloads into a single notification, and builds 50a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey * {@link PendingIntent} that launch towards {@link DownloadReceiver}. 51a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey */ 52a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkeypublic class DownloadNotifier { 53a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey 54a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey private static final int TYPE_ACTIVE = 1; 55a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey private static final int TYPE_WAITING = 2; 56a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey private static final int TYPE_COMPLETE = 3; 57a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey 58a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey private final Context mContext; 59a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey private final NotificationManager mNotifManager; 60a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey 61a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey /** 62a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey * Currently active notifications, mapped from clustering tag to timestamp 63a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey * when first shown. 64a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey * 65a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey * @see #buildNotificationTag(DownloadInfo) 66a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey */ 67a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey @GuardedBy("mActiveNotifs") 68a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey private final HashMap<String, Long> mActiveNotifs = Maps.newHashMap(); 69a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey 70a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey public DownloadNotifier(Context context) { 71a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey mContext = context; 72a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey mNotifManager = (NotificationManager) context.getSystemService( 73a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey Context.NOTIFICATION_SERVICE); 74a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey } 75a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey 76a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey /** 77a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey * Update {@link NotificationManager} to reflect the given set of 78a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey * {@link DownloadInfo}, adding, collapsing, and removing as needed. 79a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey */ 80a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey public void updateWith(Collection<DownloadInfo> downloads) { 81a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey synchronized (mActiveNotifs) { 82a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey updateWithLocked(downloads); 83a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey } 84a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey } 85a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey 86a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey private void updateWithLocked(Collection<DownloadInfo> downloads) { 87a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey final Resources res = mContext.getResources(); 88a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey 89a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey // Cluster downloads together 90a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey final Multimap<String, DownloadInfo> clustered = ArrayListMultimap.create(); 91a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey for (DownloadInfo info : downloads) { 92a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey final String tag = buildNotificationTag(info); 93a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey if (tag != null) { 94a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey clustered.put(tag, info); 95a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey } 96a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey } 97a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey 98a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey // Build notification for each cluster 99a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey for (String tag : clustered.keySet()) { 100a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey final int type = getNotificationTagType(tag); 101a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey final Collection<DownloadInfo> cluster = clustered.get(tag); 102a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey 103a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey final Notification.Builder builder = new Notification.Builder(mContext); 104a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey 105a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey // Use time when cluster was first shown to avoid shuffling 106a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey final long firstShown; 107a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey if (mActiveNotifs.containsKey(tag)) { 108a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey firstShown = mActiveNotifs.get(tag); 109a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey } else { 110a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey firstShown = System.currentTimeMillis(); 111a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey mActiveNotifs.put(tag, firstShown); 112a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey } 113a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey builder.setWhen(firstShown); 114a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey 115a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey // Show relevant icon 116a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey if (type == TYPE_ACTIVE) { 117a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey builder.setSmallIcon(android.R.drawable.stat_sys_download); 118a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey } else if (type == TYPE_WAITING) { 119a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey builder.setSmallIcon(android.R.drawable.stat_sys_warning); 120a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey } else if (type == TYPE_COMPLETE) { 121a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey builder.setSmallIcon(android.R.drawable.stat_sys_download_done); 122a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey } 123a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey 124a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey // Build action intents 125a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey if (type == TYPE_ACTIVE || type == TYPE_WAITING) { 126a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey final Intent intent = new Intent(Constants.ACTION_LIST, 127a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey null, mContext, DownloadReceiver.class); 128a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey intent.putExtra(DownloadManager.EXTRA_NOTIFICATION_CLICK_DOWNLOAD_IDS, 129a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey getDownloadIds(cluster)); 130a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey builder.setContentIntent(PendingIntent.getBroadcast(mContext, 0, intent, 0)); 131a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey builder.setOngoing(true); 132a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey 133a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey } else if (type == TYPE_COMPLETE) { 134a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey final DownloadInfo info = cluster.iterator().next(); 135a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey final Uri uri = ContentUris.withAppendedId( 136a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey Downloads.Impl.ALL_DOWNLOADS_CONTENT_URI, info.mId); 137a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey 138a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey final String action; 139a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey if (Downloads.Impl.isStatusError(info.mStatus)) { 140a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey action = Constants.ACTION_LIST; 141a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey } else { 142a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey if (info.mDestination != Downloads.Impl.DESTINATION_SYSTEMCACHE_PARTITION) { 143a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey action = Constants.ACTION_OPEN; 144a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey } else { 145a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey action = Constants.ACTION_LIST; 146a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey } 147a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey } 148a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey 149a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey final Intent intent = new Intent(action, uri, mContext, DownloadReceiver.class); 150a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey intent.putExtra(DownloadManager.EXTRA_NOTIFICATION_CLICK_DOWNLOAD_IDS, 151a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey getDownloadIds(cluster)); 152a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey builder.setContentIntent(PendingIntent.getBroadcast(mContext, 0, intent, 0)); 153a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey 154a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey final Intent hideIntent = new Intent(Constants.ACTION_HIDE, 155a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey uri, mContext, DownloadReceiver.class); 156a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey builder.setDeleteIntent(PendingIntent.getBroadcast(mContext, 0, hideIntent, 0)); 157a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey } 158a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey 159a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey // Calculate and show progress 160a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey String remainingText = null; 161a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey String percentText = null; 162a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey if (type == TYPE_ACTIVE) { 163a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey long current = 0; 164a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey long total = 0; 165a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey for (DownloadInfo info : cluster) { 166a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey if (info.mTotalBytes != -1) { 167a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey current += info.mCurrentBytes; 168a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey total += info.mTotalBytes; 169a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey } 170a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey } 171a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey 172a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey if (total > 0) { 173a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey final int percent = (int) ((current * 100) / total); 174a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey // TODO: calculate remaining time based on recent bandwidth 175a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey percentText = res.getString(R.string.download_percent, percent); 176a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey 177a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey builder.setProgress(100, percent, false); 178a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey } else { 179a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey builder.setProgress(100, 0, true); 180a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey } 181a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey } 182a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey 183a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey // Build titles and description 184a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey final Notification notif; 185a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey if (cluster.size() == 1) { 186a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey final DownloadInfo info = cluster.iterator().next(); 187a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey 188a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey builder.setContentTitle(getDownloadTitle(res, info)); 189a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey 190a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey if (type == TYPE_ACTIVE) { 191a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey if (!TextUtils.isEmpty(info.mDescription)) { 192a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey builder.setContentText(info.mDescription); 193a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey } else { 194a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey builder.setContentText(remainingText); 195a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey } 196a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey builder.setContentInfo(percentText); 197a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey 198a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey } else if (type == TYPE_WAITING) { 199a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey builder.setContentText( 200a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey res.getString(R.string.notification_need_wifi_for_size)); 201a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey 202a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey } else if (type == TYPE_COMPLETE) { 203a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey if (Downloads.Impl.isStatusError(info.mStatus)) { 204a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey builder.setContentText(res.getText(R.string.notification_download_failed)); 205a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey } else if (Downloads.Impl.isStatusSuccess(info.mStatus)) { 206a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey builder.setContentText( 207a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey res.getText(R.string.notification_download_complete)); 208a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey } 209a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey } 210a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey 211a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey notif = builder.build(); 212a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey 213a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey } else { 214a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey final Notification.InboxStyle inboxStyle = new Notification.InboxStyle(builder); 215a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey 216a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey for (DownloadInfo info : cluster) { 217a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey inboxStyle.addLine(getDownloadTitle(res, info)); 218a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey } 219a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey 220a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey if (type == TYPE_ACTIVE) { 221a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey builder.setContentTitle(res.getQuantityString( 222a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey R.plurals.notif_summary_active, cluster.size(), cluster.size())); 223a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey builder.setContentText(remainingText); 224a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey builder.setContentInfo(percentText); 225a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey inboxStyle.setSummaryText(remainingText); 226a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey 227a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey } else if (type == TYPE_WAITING) { 228a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey builder.setContentTitle(res.getQuantityString( 229a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey R.plurals.notif_summary_waiting, cluster.size(), cluster.size())); 230a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey builder.setContentText( 231a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey res.getString(R.string.notification_need_wifi_for_size)); 232a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey inboxStyle.setSummaryText( 233a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey res.getString(R.string.notification_need_wifi_for_size)); 234a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey } 235a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey 236a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey notif = inboxStyle.build(); 237a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey } 238a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey 239a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey mNotifManager.notify(tag, 0, notif); 240a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey } 241a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey 242a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey // Remove stale tags that weren't renewed 243a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey final Iterator<String> it = mActiveNotifs.keySet().iterator(); 244a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey while (it.hasNext()) { 245a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey final String tag = it.next(); 246a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey if (!clustered.containsKey(tag)) { 247a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey mNotifManager.cancel(tag, 0); 248a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey it.remove(); 249a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey } 250a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey } 251a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey } 252a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey 253a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey private static CharSequence getDownloadTitle(Resources res, DownloadInfo info) { 254a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey if (!TextUtils.isEmpty(info.mTitle)) { 255a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey return info.mTitle; 256a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey } else { 257a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey return res.getString(R.string.download_unknown_title); 258a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey } 259a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey } 260a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey 261a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey private long[] getDownloadIds(Collection<DownloadInfo> infos) { 262a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey final long[] ids = new long[infos.size()]; 263a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey int i = 0; 264a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey for (DownloadInfo info : infos) { 265a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey ids[i++] = info.mId; 266a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey } 267a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey return ids; 268a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey } 269a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey 270a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey /** 271a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey * Build tag used for collapsing several {@link DownloadInfo} into a single 272a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey * {@link Notification}. 273a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey */ 274a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey private static String buildNotificationTag(DownloadInfo info) { 275a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey if (info.mStatus == Downloads.Impl.STATUS_QUEUED_FOR_WIFI) { 276a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey return TYPE_WAITING + ":" + info.mPackage; 277a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey } else if (isActiveAndVisible(info)) { 278a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey return TYPE_ACTIVE + ":" + info.mPackage; 279a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey } else if (isCompleteAndVisible(info)) { 280a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey // Complete downloads always have unique notifs 281a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey return TYPE_COMPLETE + ":" + info.mId; 282a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey } else { 283a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey return null; 284a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey } 285a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey } 286a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey 287a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey /** 288a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey * Return the cluster type of the given tag, as created by 289a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey * {@link #buildNotificationTag(DownloadInfo)}. 290a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey */ 291a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey private static int getNotificationTagType(String tag) { 292a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey return Integer.parseInt(tag.substring(0, tag.indexOf(':'))); 293a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey } 294a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey 295a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey private static boolean isActiveAndVisible(DownloadInfo download) { 296a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey return Downloads.Impl.isStatusInformational(download.mStatus) && 297a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey (download.mVisibility == VISIBILITY_VISIBLE 298a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey || download.mVisibility == VISIBILITY_VISIBLE_NOTIFY_COMPLETED); 299a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey } 300a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey 301a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey private static boolean isCompleteAndVisible(DownloadInfo download) { 302a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey return Downloads.Impl.isStatusCompleted(download.mStatus) && 303a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey (download.mVisibility == VISIBILITY_VISIBLE_NOTIFY_COMPLETED 304a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey || download.mVisibility == VISIBILITY_VISIBLE_NOTIFY_ONLY_COMPLETION); 305a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey } 306a40a349c0107660bfb4004467550725a3ca3ec97Jeff Sharkey} 307