eclair snapshot
[android/platform/packages/providers/DownloadProvider.git] / src / com / android / providers / downloads / DownloadNotification.java
1 /*
2  * Copyright (C) 2008 The Android Open Source Project
3  *
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
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16
17 package com.android.providers.downloads;
18
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;
28
29 import java.util.HashMap;
30
31 /**
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.
36  *
37  */
38 class DownloadNotification {
39
40     Context mContext;
41     public NotificationManager mNotificationMgr;
42     HashMap <String, NotificationItem> mNotifications;
43     
44     static final String LOGTAG = "DownloadNotification";
45     static final String WHERE_RUNNING = 
46         "(" + Downloads.COLUMN_STATUS + " >= '100') AND (" +
47         Downloads.COLUMN_STATUS + " <= '199') AND (" +
48         Downloads.COLUMN_VISIBILITY + " IS NULL OR " +
49         Downloads.COLUMN_VISIBILITY + " == '" + Downloads.VISIBILITY_VISIBLE + "' OR " +
50         Downloads.COLUMN_VISIBILITY +
51             " == '" + Downloads.VISIBILITY_VISIBLE_NOTIFY_COMPLETED + "')";
52     static final String WHERE_COMPLETED =
53         Downloads.COLUMN_STATUS + " >= '200' AND " +
54         Downloads.COLUMN_VISIBILITY + " == '" + Downloads.VISIBILITY_VISIBLE_NOTIFY_COMPLETED + "'";
55     
56     
57     /**
58      * This inner class is used to collate downloads that are owned by
59      * the same application. This is so that only one notification line
60      * item is used for all downloads of a given application.
61      *
62      */
63     static class NotificationItem {
64         int mId;  // This first db _id for the download for the app
65         int mTotalCurrent = 0;
66         int mTotalTotal = 0;
67         int mTitleCount = 0;
68         String mPackageName;  // App package name
69         String mDescription;
70         String[] mTitles = new String[2]; // download titles.
71         
72         /*
73          * Add a second download to this notification item.
74          */
75         void addItem(String title, int currentBytes, int totalBytes) {
76             mTotalCurrent += currentBytes;
77             if (totalBytes <= 0 || mTotalTotal == -1) {
78                 mTotalTotal = -1;
79             } else {
80                 mTotalTotal += totalBytes;
81             }
82             if (mTitleCount < 2) {
83                 mTitles[mTitleCount] = title;
84             }
85             mTitleCount++;
86         }
87     }
88         
89     
90     /**
91      * Constructor
92      * @param ctx The context to use to obtain access to the 
93      *            Notification Service
94      */
95     DownloadNotification(Context ctx) {
96         mContext = ctx;
97         mNotificationMgr = (NotificationManager) mContext
98                 .getSystemService(Context.NOTIFICATION_SERVICE);
99         mNotifications = new HashMap<String, NotificationItem>();
100     }
101     
102     /*
103      * Update the notification ui. 
104      */
105     public void updateNotification() {
106         updateActiveNotification();
107         updateCompletedNotification();
108     }
109
110     private void updateActiveNotification() {
111         // Active downloads
112         Cursor c = mContext.getContentResolver().query(
113                 Downloads.CONTENT_URI, new String [] {
114                         Downloads._ID,
115                         Downloads.COLUMN_TITLE,
116                         Downloads.COLUMN_DESCRIPTION,
117                         Downloads.COLUMN_NOTIFICATION_PACKAGE,
118                         Downloads.COLUMN_NOTIFICATION_CLASS,
119                         Downloads.COLUMN_CURRENT_BYTES,
120                         Downloads.COLUMN_TOTAL_BYTES,
121                         Downloads.COLUMN_STATUS, Downloads._DATA
122                 },
123                 WHERE_RUNNING, null, Downloads._ID);
124         
125         if (c == null) {
126             return;
127         }
128         
129         // Columns match projection in query above
130         final int idColumn = 0;
131         final int titleColumn = 1;
132         final int descColumn = 2;
133         final int ownerColumn = 3;
134         final int classOwnerColumn = 4;
135         final int currentBytesColumn = 5;
136         final int totalBytesColumn = 6;
137         final int statusColumn = 7;
138         final int filenameColumnId = 8;
139
140         // Collate the notifications
141         mNotifications.clear();
142         for (c.moveToFirst(); !c.isAfterLast(); c.moveToNext()) {
143             String packageName = c.getString(ownerColumn);
144             int max = c.getInt(totalBytesColumn);
145             int progress = c.getInt(currentBytesColumn);
146             String title = c.getString(titleColumn);
147             if (title == null || title.length() == 0) {
148                 title = mContext.getResources().getString(
149                         R.string.download_unknown_title);
150             }
151             if (mNotifications.containsKey(packageName)) {
152                 mNotifications.get(packageName).addItem(title, progress, max);
153             } else {
154                 NotificationItem item = new NotificationItem();
155                 item.mId = c.getInt(idColumn);
156                 item.mPackageName = packageName;
157                 item.mDescription = c.getString(descColumn);
158                 String className = c.getString(classOwnerColumn);
159                 item.addItem(title, progress, max);
160                 mNotifications.put(packageName, item);
161             }
162             
163         }
164         c.close();
165         
166         // Add the notifications
167         for (NotificationItem item : mNotifications.values()) {
168             // Build the notification object
169             Notification n = new Notification();
170             n.icon = android.R.drawable.stat_sys_download;
171
172             n.flags |= Notification.FLAG_ONGOING_EVENT;
173             
174             // Build the RemoteView object
175             RemoteViews expandedView = new RemoteViews(
176                     "com.android.providers.downloads",
177                     R.layout.status_bar_ongoing_event_progress_bar);
178             StringBuilder title = new StringBuilder(item.mTitles[0]);
179             if (item.mTitleCount > 1) {
180                 title.append(mContext.getString(R.string.notification_filename_separator));
181                 title.append(item.mTitles[1]);
182                 n.number = item.mTitleCount;
183                 if (item.mTitleCount > 2) {
184                     title.append(mContext.getString(R.string.notification_filename_extras,
185                             new Object[] { Integer.valueOf(item.mTitleCount - 2) }));
186                 }
187             } else {
188                 expandedView.setTextViewText(R.id.description, 
189                         item.mDescription);
190             }
191             expandedView.setTextViewText(R.id.title, title);
192             expandedView.setProgressBar(R.id.progress_bar, 
193                     item.mTotalTotal,
194                     item.mTotalCurrent,
195                     item.mTotalTotal == -1);
196             expandedView.setTextViewText(R.id.progress_text, 
197                     getDownloadingText(item.mTotalTotal, item.mTotalCurrent));
198             expandedView.setImageViewResource(R.id.appIcon,
199                     android.R.drawable.stat_sys_download);
200             n.contentView = expandedView;
201
202             Intent intent = new Intent(Constants.ACTION_LIST);
203             intent.setClassName("com.android.providers.downloads",
204                     DownloadReceiver.class.getName());
205             intent.setData(Uri.parse(Downloads.CONTENT_URI + "/" + item.mId));
206             intent.putExtra("multiple", item.mTitleCount > 1);
207
208             n.contentIntent = PendingIntent.getBroadcast(mContext, 0, intent, 0);
209
210             mNotificationMgr.notify(item.mId, n);
211             
212         }
213     }
214
215     private void updateCompletedNotification() {
216         // Completed downloads
217         Cursor c = mContext.getContentResolver().query(
218                 Downloads.CONTENT_URI, new String [] {
219                         Downloads._ID,
220                         Downloads.COLUMN_TITLE,
221                         Downloads.COLUMN_DESCRIPTION,
222                         Downloads.COLUMN_NOTIFICATION_PACKAGE,
223                         Downloads.COLUMN_NOTIFICATION_CLASS,
224                         Downloads.COLUMN_CURRENT_BYTES,
225                         Downloads.COLUMN_TOTAL_BYTES,
226                         Downloads.COLUMN_STATUS,
227                         Downloads._DATA,
228                         Downloads.COLUMN_LAST_MODIFICATION,
229                         Downloads.COLUMN_DESTINATION
230                 },
231                 WHERE_COMPLETED, null, Downloads._ID);
232         
233         if (c == null) {
234             return;
235         }
236         
237         // Columns match projection in query above
238         final int idColumn = 0;
239         final int titleColumn = 1;
240         final int descColumn = 2;
241         final int ownerColumn = 3;
242         final int classOwnerColumn = 4;
243         final int currentBytesColumn = 5;
244         final int totalBytesColumn = 6;
245         final int statusColumn = 7;
246         final int filenameColumnId = 8;
247         final int lastModColumnId = 9;
248         final int destinationColumnId = 10;
249
250         for (c.moveToFirst(); !c.isAfterLast(); c.moveToNext()) {
251             // Add the notifications
252             Notification n = new Notification();
253             n.icon = android.R.drawable.stat_sys_download_done;
254
255             String title = c.getString(titleColumn);
256             if (title == null || title.length() == 0) {
257                 title = mContext.getResources().getString(
258                         R.string.download_unknown_title);
259             }
260             Uri contentUri = Uri.parse(Downloads.CONTENT_URI + "/" + c.getInt(idColumn));
261             String caption;
262             Intent intent;
263             if (Downloads.isStatusError(c.getInt(statusColumn))) {
264                 caption = mContext.getResources()
265                         .getString(R.string.notification_download_failed);
266                 intent = new Intent(Constants.ACTION_LIST);
267             } else {
268                 caption = mContext.getResources()
269                         .getString(R.string.notification_download_complete);
270                 if (c.getInt(destinationColumnId) == Downloads.DESTINATION_EXTERNAL) {
271                     intent = new Intent(Constants.ACTION_OPEN);
272                 } else {
273                     intent = new Intent(Constants.ACTION_LIST);
274                 }
275             }
276             intent.setClassName("com.android.providers.downloads",
277                     DownloadReceiver.class.getName());
278             intent.setData(contentUri);
279             n.setLatestEventInfo(mContext, title, caption,
280                     PendingIntent.getBroadcast(mContext, 0, intent, 0));
281
282             intent = new Intent(Constants.ACTION_HIDE);
283             intent.setClassName("com.android.providers.downloads",
284                     DownloadReceiver.class.getName());
285             intent.setData(contentUri);
286             n.deleteIntent = PendingIntent.getBroadcast(mContext, 0, intent, 0);
287
288             n.when = c.getLong(lastModColumnId);
289
290             mNotificationMgr.notify(c.getInt(idColumn), n);
291         }
292         c.close();
293     }
294
295     /*
296      * Helper function to build the downloading text.
297      */
298     private String getDownloadingText(long totalBytes, long currentBytes) {
299         if (totalBytes <= 0) {
300             return "";
301         }
302         long progress = currentBytes * 100 / totalBytes;
303         StringBuilder sb = new StringBuilder();
304         sb.append(progress);
305         sb.append('%');
306         return sb.toString();
307     }
308     
309 }