BluetoothOppNotification.java revision 6769b59d715ea98bd72eafcfea9acd2714a887da
1/*
2 * Copyright (c) 2008-2009, Motorola, Inc.
3 *
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are met:
8 *
9 * - Redistributions of source code must retain the above copyright notice,
10 * this list of conditions and the following disclaimer.
11 *
12 * - Redistributions in binary form must reproduce the above copyright notice,
13 * this list of conditions and the following disclaimer in the documentation
14 * and/or other materials provided with the distribution.
15 *
16 * - Neither the name of the Motorola, Inc. nor the names of its contributors
17 * may be used to endorse or promote products derived from this software
18 * without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
24 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30 * POSSIBILITY OF SUCH DAMAGE.
31 */
32
33package com.android.bluetooth.opp;
34
35import com.android.bluetooth.R;
36
37import android.content.Context;
38import android.app.Notification;
39import android.app.NotificationManager;
40import android.app.PendingIntent;
41import android.content.Intent;
42import android.database.Cursor;
43import android.net.Uri;
44import android.util.Log;
45import android.widget.RemoteViews;
46import android.widget.Toast;
47import java.util.HashMap;
48
49/**
50 * This class handles the updating of the Notification Manager for the cases
51 * where there is an ongoing transfer, incoming transfer need confirm and
52 * complete (successful or failed) transfer.
53 */
54class BluetoothOppNotification {
55    private static final String TAG = "BluetoothOppNotification";
56
57    Context mContext;
58
59    public NotificationManager mNotificationMgr;
60
61    HashMap<String, NotificationItem> mNotifications;
62
63    static final String status = "(" + BluetoothShare.STATUS + " == '192'" + ")";
64
65    static final String visible = "(" + BluetoothShare.VISIBILITY + " IS NULL OR "
66            + BluetoothShare.VISIBILITY + " == '" + BluetoothShare.VISIBILITY_VISIBLE + "'" + ")";
67
68    static final String confirm = "(" + BluetoothShare.USER_CONFIRMATION + " == '"
69            + BluetoothShare.USER_CONFIRMATION_CONFIRMED + "' OR "
70            + BluetoothShare.USER_CONFIRMATION + " == '"
71            + BluetoothShare.USER_CONFIRMATION_AUTO_CONFIRMED + "'" + ")";
72
73    static final String WHERE_RUNNING = status + " AND " + visible + " AND " + confirm;
74
75    static final String WHERE_COMPLETED = BluetoothShare.STATUS + " >= '200' AND " + visible;
76
77    static final String WHERE_CONFIRM_PENDING = BluetoothShare.USER_CONFIRMATION + " == '"
78            + BluetoothShare.USER_CONFIRMATION_PENDING + "'" + " AND " + visible;
79
80    /**
81     * This inner class is used to describe some properties for one transfer.
82     */
83    static class NotificationItem {
84        int id; // This first field _id in db;
85
86        int direction; // to indicate sending or receiving
87
88        int totalCurrent = 0; // current transfer bytes
89
90        int totalTotal = 0; // total bytes for current transfer
91
92        String description; // the text above progress bar
93    }
94
95    /**
96     * Constructor
97     * @param ctx The context to use to obtain access to the Notification Service
98     */
99    BluetoothOppNotification(Context ctx) {
100        mContext = ctx;
101        mNotificationMgr = (NotificationManager)mContext
102                .getSystemService(Context.NOTIFICATION_SERVICE);
103        mNotifications = new HashMap<String, NotificationItem>();
104    }
105
106    /**
107     * Update the notification ui.
108     */
109    public void updateNotification() {
110        updateActiveNotification();
111        updateCompletedNotification();
112        updateIncomingFileConfirmNotification();
113    }
114
115    private void updateActiveNotification() {
116        // Active transfers
117        Cursor cursor = mContext.getContentResolver().query(BluetoothShare.CONTENT_URI, null,
118                WHERE_RUNNING, null, BluetoothShare._ID);
119        if (cursor == null) {
120            return;
121        }
122
123        // Collate the notifications
124        mNotifications.clear();
125        for (cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) {
126            int timeStamp = cursor.getInt(cursor.getColumnIndexOrThrow(BluetoothShare.TIMESTAMP));
127            int dir = cursor.getInt(cursor.getColumnIndexOrThrow(BluetoothShare.DIRECTION));
128            int id = cursor.getInt(cursor.getColumnIndexOrThrow(BluetoothShare._ID));
129            int total = cursor.getInt(cursor.getColumnIndexOrThrow(BluetoothShare.TOTAL_BYTES));
130            int current = cursor.getInt(cursor.getColumnIndexOrThrow(BluetoothShare.CURRENT_BYTES));
131
132            String fileName = cursor.getString(cursor.getColumnIndexOrThrow(BluetoothShare._DATA));
133            if (fileName == null) {
134                fileName = cursor.getString(cursor
135                        .getColumnIndexOrThrow(BluetoothShare.FILENAME_HINT));
136            }
137            if (fileName == null) {
138                fileName = mContext.getString(R.string.unknown_file);
139            }
140
141            String batchID = Long.toString(timeStamp);
142
143            // sending objects in one batch has same timeStamp
144            if (mNotifications.containsKey(batchID)) {
145                // NOTE: currently no such case
146                // Batch sending case
147            } else {
148                NotificationItem item = new NotificationItem();
149                item.id = id;
150                item.direction = dir;
151                if (item.direction == BluetoothShare.DIRECTION_OUTBOUND) {
152                    item.description = mContext.getString(R.string.notification_sending).replace(
153                            "%s", fileName);
154                } else if (item.direction == BluetoothShare.DIRECTION_INBOUND) {
155                    item.description = mContext.getString(R.string.notification_receiving).replace(
156                            "%s", fileName);
157                } else {
158                    if (Constants.LOGVV) {
159                        Log.v(TAG, "mDirection ERROR!");
160                    }
161                }
162                item.totalCurrent = current;
163                item.totalTotal = total;
164
165                mNotifications.put(batchID, item);
166
167                if (Constants.LOGVV) {
168                    Log.v(TAG, "ID=" + item.id + "; batchID=" + batchID + "; totoalCurrent"
169                            + item.totalCurrent + "; totalTotal=" + item.totalTotal);
170                }
171            }
172        }
173        cursor.close();
174
175        // Add the notifications
176        for (NotificationItem item : mNotifications.values()) {
177            // Build the RemoteView object
178            RemoteViews expandedView = new RemoteViews(Constants.THIS_PACKAGE_NAME,
179                    R.layout.status_bar_ongoing_event_progress_bar);
180
181            expandedView.setTextViewText(R.id.description, item.description);
182
183            expandedView.setProgressBar(R.id.progress_bar, item.totalTotal, item.totalCurrent,
184                    item.totalTotal == -1);
185
186            expandedView.setTextViewText(R.id.progress_text, BluetoothOppUtility
187                    .formatProgressText(item.totalTotal, item.totalCurrent));
188
189            // Build the notification object
190            Notification n = new Notification();
191            if (item.direction == BluetoothShare.DIRECTION_OUTBOUND) {
192                n.icon = android.R.drawable.stat_sys_upload;
193                expandedView.setImageViewResource(R.id.appIcon, android.R.drawable.stat_sys_upload);
194            } else if (item.direction == BluetoothShare.DIRECTION_INBOUND) {
195                n.icon = android.R.drawable.stat_sys_download;
196                expandedView.setImageViewResource(R.id.appIcon,
197                        android.R.drawable.stat_sys_download);
198            } else {
199                if (Constants.LOGVV) {
200                    Log.v(TAG, "mDirection ERROR!");
201                }
202            }
203
204            n.flags |= Notification.FLAG_ONGOING_EVENT;
205            n.contentView = expandedView;
206
207            Intent intent = new Intent(Constants.ACTION_LIST);
208            intent.setClassName(Constants.THIS_PACKAGE_NAME, BluetoothOppReceiver.class.getName());
209            intent.setData(Uri.parse(BluetoothShare.CONTENT_URI + "/" + item.id));
210
211            n.contentIntent = PendingIntent.getBroadcast(mContext, 0, intent, 0);
212            mNotificationMgr.notify(item.id, n);
213        }
214    }
215
216    private void updateCompletedNotification() {
217        Cursor cursor = mContext.getContentResolver().query(BluetoothShare.CONTENT_URI, null,
218                WHERE_COMPLETED, null, BluetoothShare._ID);
219        if (cursor == null) {
220            return;
221        }
222
223        for (cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) {
224            // Add the notifications
225            int timeStamp = cursor.getInt(cursor.getColumnIndexOrThrow(BluetoothShare.TIMESTAMP));
226            int dir = cursor.getInt(cursor.getColumnIndexOrThrow(BluetoothShare.DIRECTION));
227            int id = cursor.getInt(cursor.getColumnIndexOrThrow(BluetoothShare._ID));
228            int status = cursor.getInt(cursor.getColumnIndexOrThrow(BluetoothShare.STATUS));
229
230            String fileName = cursor.getString(cursor.getColumnIndexOrThrow(BluetoothShare._DATA));
231            if (fileName == null) {
232                fileName = cursor.getString(cursor
233                        .getColumnIndexOrThrow(BluetoothShare.FILENAME_HINT));
234            }
235            if (fileName == null) {
236                fileName = mContext.getString(R.string.unknown_file);
237            }
238
239            String title;
240            String caption;
241            Uri contentUri = Uri.parse(BluetoothShare.CONTENT_URI + "/" + id);
242
243            Notification n = new Notification();
244            if (BluetoothShare.isStatusError(status)) {
245                if (dir == BluetoothShare.DIRECTION_OUTBOUND) {
246                    title = mContext.getString(R.string.notification_sent_fail).replace("%s",
247                            fileName);
248                } else {
249                    title = mContext.getString(R.string.notification_received_fail).replace("%s",
250                            fileName);
251                }
252                caption = mContext.getString(R.string.download_fail_line3).replace("%s",
253                        BluetoothOppUtility.getStatusDescription(mContext, status));
254                n.icon = android.R.drawable.stat_notify_error;
255            } else {
256                if (dir == BluetoothShare.DIRECTION_OUTBOUND) {
257                    title = mContext.getString(R.string.notification_sent).replace("%s", fileName);
258                    n.icon = android.R.drawable.stat_sys_upload_done;
259                } else {
260                    title = mContext.getString(R.string.notification_received).replace("%s",
261                            fileName);
262                    n.icon = android.R.drawable.stat_sys_download_done;
263                }
264                caption = mContext.getString(R.string.notification_sent_complete);
265            }
266            Intent intent = new Intent(Constants.ACTION_OPEN);
267            intent.setClassName(Constants.THIS_PACKAGE_NAME, BluetoothOppReceiver.class.getName());
268            intent.setData(contentUri);
269
270            n.setLatestEventInfo(mContext, title, caption, PendingIntent.getBroadcast(mContext, 0,
271                    intent, 0));
272
273            intent = new Intent(Constants.ACTION_HIDE);
274            intent.setClassName(Constants.THIS_PACKAGE_NAME, BluetoothOppReceiver.class.getName());
275            intent.setData(contentUri);
276            n.deleteIntent = PendingIntent.getBroadcast(mContext, 0, intent, 0);
277
278            n.when = timeStamp;
279
280            mNotificationMgr.notify(id, n);
281        }
282        cursor.close();
283    }
284
285    private void updateIncomingFileConfirmNotification() {
286        Cursor cursor = mContext.getContentResolver().query(BluetoothShare.CONTENT_URI, null,
287                WHERE_CONFIRM_PENDING, null, BluetoothShare._ID);
288
289        if (cursor == null) {
290            return;
291        }
292
293        for (cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) {
294            String title = mContext.getString(R.string.incoming_file_confirm_Notification_title);
295            String caption = mContext
296                    .getString(R.string.incoming_file_confirm_Notification_caption);
297            int id = cursor.getInt(cursor.getColumnIndexOrThrow(BluetoothShare._ID));
298            int timeStamp = cursor.getInt(cursor.getColumnIndexOrThrow(BluetoothShare.TIMESTAMP));
299            Uri contentUri = Uri.parse(BluetoothShare.CONTENT_URI + "/" + id);
300
301            Notification n = new Notification();
302            n.icon = R.drawable.stat_sys_data_bt;
303            n.flags |= Notification.FLAG_ONLY_ALERT_ONCE;
304            n.defaults = Notification.DEFAULT_SOUND;
305            n.tickerText = title;
306            Intent intent = new Intent(Constants.ACTION_INCOMING_FILE_CONFIRM);
307            intent.setClassName(Constants.THIS_PACKAGE_NAME, BluetoothOppReceiver.class.getName());
308            intent.setData(contentUri);
309            n.setLatestEventInfo(mContext, title, caption, PendingIntent.getBroadcast(mContext, 0,
310                    intent, 0));
311
312            intent = new Intent(Constants.ACTION_HIDE);
313            intent.setClassName(Constants.THIS_PACKAGE_NAME, BluetoothOppReceiver.class.getName());
314            intent.setData(contentUri);
315            n.deleteIntent = PendingIntent.getBroadcast(mContext, 0, intent, 0);
316
317            n.when = timeStamp;
318            mNotificationMgr.notify(id, n);
319        }
320        cursor.close();
321    }
322}
323