2 * Copyright (C) 2008 The Android Open Source Project
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 package com.android.providers.downloads;
19 import android.app.Notification;
20 import android.app.NotificationManager;
21 import android.app.PendingIntent;
22 import android.content.Context;
23 import android.content.Intent;
24 import android.database.Cursor;
25 import android.net.Uri;
26 import android.provider.Downloads;
27 import android.widget.RemoteViews;
29 import java.util.HashMap;
32 * This class handles the updating of the Notification Manager for the
33 * cases where there is an ongoing download. Once the download is complete
34 * (be it successful or unsuccessful) it is no longer the responsibility
35 * of this component to show the download in the notification manager.
38 class DownloadNotification {
41 public NotificationManager mNotificationMgr;
42 HashMap <String, NotificationItem> mNotifications;
44 static final String LOGTAG = "DownloadNotification";
45 static final String WHERE_RUNNING =
46 "(" + Downloads.Impl.COLUMN_STATUS + " >= '100') AND (" +
47 Downloads.Impl.COLUMN_STATUS + " <= '199') AND (" +
48 Downloads.Impl.COLUMN_VISIBILITY + " IS NULL OR " +
49 Downloads.Impl.COLUMN_VISIBILITY + " == '" + Downloads.Impl.VISIBILITY_VISIBLE + "' OR " +
50 Downloads.Impl.COLUMN_VISIBILITY +
51 " == '" + Downloads.Impl.VISIBILITY_VISIBLE_NOTIFY_COMPLETED + "')";
52 static final String WHERE_COMPLETED =
53 Downloads.Impl.COLUMN_STATUS + " >= '200' AND " +
54 Downloads.Impl.COLUMN_VISIBILITY +
55 " == '" + Downloads.Impl.VISIBILITY_VISIBLE_NOTIFY_COMPLETED + "'";
59 * This inner class is used to collate downloads that are owned by
60 * the same application. This is so that only one notification line
61 * item is used for all downloads of a given application.
64 static class NotificationItem {
65 int mId; // This first db _id for the download for the app
66 int mTotalCurrent = 0;
69 String mPackageName; // App package name
71 String[] mTitles = new String[2]; // download titles.
74 * Add a second download to this notification item.
76 void addItem(String title, int currentBytes, int totalBytes) {
77 mTotalCurrent += currentBytes;
78 if (totalBytes <= 0 || mTotalTotal == -1) {
81 mTotalTotal += totalBytes;
83 if (mTitleCount < 2) {
84 mTitles[mTitleCount] = title;
93 * @param ctx The context to use to obtain access to the
94 * Notification Service
96 DownloadNotification(Context ctx) {
98 mNotificationMgr = (NotificationManager) mContext
99 .getSystemService(Context.NOTIFICATION_SERVICE);
100 mNotifications = new HashMap<String, NotificationItem>();
104 * Update the notification ui.
106 public void updateNotification() {
107 updateActiveNotification();
108 updateCompletedNotification();
111 private void updateActiveNotification() {
113 Cursor c = mContext.getContentResolver().query(
114 Downloads.Impl.CONTENT_URI, new String [] {
116 Downloads.Impl.COLUMN_TITLE,
117 Downloads.Impl.COLUMN_DESCRIPTION,
118 Downloads.Impl.COLUMN_NOTIFICATION_PACKAGE,
119 Downloads.Impl.COLUMN_NOTIFICATION_CLASS,
120 Downloads.Impl.COLUMN_CURRENT_BYTES,
121 Downloads.Impl.COLUMN_TOTAL_BYTES,
122 Downloads.Impl.COLUMN_STATUS, Downloads.Impl._DATA
124 WHERE_RUNNING, null, Downloads.Impl._ID);
130 // Columns match projection in query above
131 final int idColumn = 0;
132 final int titleColumn = 1;
133 final int descColumn = 2;
134 final int ownerColumn = 3;
135 final int classOwnerColumn = 4;
136 final int currentBytesColumn = 5;
137 final int totalBytesColumn = 6;
138 final int statusColumn = 7;
139 final int filenameColumnId = 8;
141 // Collate the notifications
142 mNotifications.clear();
143 for (c.moveToFirst(); !c.isAfterLast(); c.moveToNext()) {
144 String packageName = c.getString(ownerColumn);
145 int max = c.getInt(totalBytesColumn);
146 int progress = c.getInt(currentBytesColumn);
147 long id = c.getLong(idColumn);
148 String title = c.getString(titleColumn);
149 if (title == null || title.length() == 0) {
150 String filename = c.getString(filenameColumnId);
151 if (filename == null) {
152 title = mContext.getResources().getString(
153 R.string.download_unknown_title);
155 title = Downloads.Impl.createTitleFromFilename(mContext,
159 if (mNotifications.containsKey(packageName)) {
160 mNotifications.get(packageName).addItem(title, progress, max);
162 NotificationItem item = new NotificationItem();
164 item.mPackageName = packageName;
165 item.mDescription = c.getString(descColumn);
166 String className = c.getString(classOwnerColumn);
167 item.addItem(title, progress, max);
168 mNotifications.put(packageName, item);
174 // Add the notifications
175 for (NotificationItem item : mNotifications.values()) {
176 // Build the notification object
177 Notification n = new Notification();
178 n.icon = android.R.drawable.stat_sys_download;
180 n.flags |= Notification.FLAG_ONGOING_EVENT;
182 // Build the RemoteView object
183 RemoteViews expandedView = new RemoteViews(
184 "com.android.providers.downloads",
185 R.layout.status_bar_ongoing_event_progress_bar);
186 StringBuilder title = new StringBuilder(item.mTitles[0]);
187 if (item.mTitleCount > 1) {
188 title.append(mContext.getString(R.string.notification_filename_separator));
189 title.append(item.mTitles[1]);
190 n.number = item.mTitleCount;
191 if (item.mTitleCount > 2) {
192 title.append(mContext.getString(R.string.notification_filename_extras,
193 new Object[] { Integer.valueOf(item.mTitleCount - 2) }));
196 expandedView.setTextViewText(R.id.description,
199 expandedView.setTextViewText(R.id.title, title);
200 expandedView.setProgressBar(R.id.progress_bar,
203 item.mTotalTotal == -1);
204 expandedView.setTextViewText(R.id.progress_text,
205 getDownloadingText(item.mTotalTotal, item.mTotalCurrent));
206 expandedView.setImageViewResource(R.id.appIcon,
207 android.R.drawable.stat_sys_download);
208 n.contentView = expandedView;
210 Intent intent = new Intent(Constants.ACTION_LIST);
211 intent.setClassName("com.android.providers.downloads",
212 DownloadReceiver.class.getName());
213 intent.setData(Uri.parse(Downloads.Impl.CONTENT_URI + "/" + item.mId));
214 intent.putExtra("multiple", item.mTitleCount > 1);
216 n.contentIntent = PendingIntent.getBroadcast(mContext, 0, intent, 0);
218 mNotificationMgr.notify(item.mId, n);
223 private void updateCompletedNotification() {
224 // Completed downloads
225 Cursor c = mContext.getContentResolver().query(
226 Downloads.Impl.CONTENT_URI, new String [] {
228 Downloads.Impl.COLUMN_TITLE,
229 Downloads.Impl.COLUMN_DESCRIPTION,
230 Downloads.Impl.COLUMN_NOTIFICATION_PACKAGE,
231 Downloads.Impl.COLUMN_NOTIFICATION_CLASS,
232 Downloads.Impl.COLUMN_CURRENT_BYTES,
233 Downloads.Impl.COLUMN_TOTAL_BYTES,
234 Downloads.Impl.COLUMN_STATUS,
235 Downloads.Impl._DATA,
236 Downloads.Impl.COLUMN_LAST_MODIFICATION,
237 Downloads.Impl.COLUMN_DESTINATION
239 WHERE_COMPLETED, null, Downloads.Impl._ID);
245 // Columns match projection in query above
246 final int idColumn = 0;
247 final int titleColumn = 1;
248 final int descColumn = 2;
249 final int ownerColumn = 3;
250 final int classOwnerColumn = 4;
251 final int currentBytesColumn = 5;
252 final int totalBytesColumn = 6;
253 final int statusColumn = 7;
254 final int filenameColumnId = 8;
255 final int lastModColumnId = 9;
256 final int destinationColumnId = 10;
258 for (c.moveToFirst(); !c.isAfterLast(); c.moveToNext()) {
259 // Add the notifications
260 Notification n = new Notification();
261 n.icon = android.R.drawable.stat_sys_download_done;
263 long id = c.getLong(idColumn);
264 String title = c.getString(titleColumn);
265 if (title == null || title.length() == 0) {
266 String filename = c.getString(filenameColumnId);
267 if (filename == null) {
268 title = mContext.getResources().getString(
269 R.string.download_unknown_title);
271 title = Downloads.Impl.createTitleFromFilename(mContext,
275 Uri contentUri = Uri.parse(Downloads.Impl.CONTENT_URI + "/" + id);
278 if (Downloads.Impl.isStatusError(c.getInt(statusColumn))) {
279 caption = mContext.getResources()
280 .getString(R.string.notification_download_failed);
281 intent = new Intent(Constants.ACTION_LIST);
283 caption = mContext.getResources()
284 .getString(R.string.notification_download_complete);
285 if (c.getInt(destinationColumnId) == Downloads.Impl.DESTINATION_EXTERNAL) {
286 intent = new Intent(Constants.ACTION_OPEN);
288 intent = new Intent(Constants.ACTION_LIST);
291 intent.setClassName("com.android.providers.downloads",
292 DownloadReceiver.class.getName());
293 intent.setData(contentUri);
295 n.when = c.getLong(lastModColumnId);
296 n.setLatestEventInfo(mContext, title, caption,
297 PendingIntent.getBroadcast(mContext, 0, intent, 0));
299 intent = new Intent(Constants.ACTION_HIDE);
300 intent.setClassName("com.android.providers.downloads",
301 DownloadReceiver.class.getName());
302 intent.setData(contentUri);
303 n.deleteIntent = PendingIntent.getBroadcast(mContext, 0, intent, 0);
305 mNotificationMgr.notify(c.getInt(idColumn), n);
311 * Helper function to build the downloading text.
313 private String getDownloadingText(long totalBytes, long currentBytes) {
314 if (totalBytes <= 0) {
317 long progress = currentBytes * 100 / totalBytes;
318 StringBuilder sb = new StringBuilder();
321 return sb.toString();