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