BluetoothOppNotification.java revision 464d9d216ab1a6cf192e3e526596073c7e35e4a7
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     *
98     * @param ctx The context to use to obtain access to the Notification
99     *            Service
100     */
101    BluetoothOppNotification(Context ctx) {
102        mContext = ctx;
103        mNotificationMgr = (NotificationManager)mContext
104                .getSystemService(Context.NOTIFICATION_SERVICE);
105        mNotifications = new HashMap<String, NotificationItem>();
106    }
107
108    /**
109     * Update the notification ui.
110     */
111    public void updateNotification() {
112        updateActiveNotification();
113        updateCompletedNotification();
114        updateIncomingFileConfirmNotification();
115    }
116
117    private void updateActiveNotification() {
118        // Active transfers
119        Cursor cursor = mContext.getContentResolver().query(BluetoothShare.CONTENT_URI, null,
120                WHERE_RUNNING, null, BluetoothShare._ID);
121        if (cursor == null) {
122            return;
123        }
124
125        // Collate the notifications
126        mNotifications.clear();
127        for (cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) {
128            int timeStamp = cursor.getInt(cursor.getColumnIndexOrThrow(BluetoothShare.TIMESTAMP));
129            int dir = cursor.getInt(cursor.getColumnIndexOrThrow(BluetoothShare.DIRECTION));
130            int id = cursor.getInt(cursor.getColumnIndexOrThrow(BluetoothShare._ID));
131            int total = cursor.getInt(cursor.getColumnIndexOrThrow(BluetoothShare.TOTAL_BYTES));
132            int current = cursor.getInt(cursor.getColumnIndexOrThrow(BluetoothShare.CURRENT_BYTES));
133
134            String fileName = cursor.getString(cursor.getColumnIndexOrThrow(BluetoothShare._DATA));
135            if (fileName == null) {
136                fileName = cursor.getString(cursor
137                        .getColumnIndexOrThrow(BluetoothShare.FILENAME_HINT));
138            }
139            if (fileName == null) {
140                fileName = mContext.getString(R.string.unknown_file);
141            }
142
143            String batchID = Long.toString(timeStamp);
144
145            // sending objects in one batch has same timeStamp
146            if (mNotifications.containsKey(batchID)) {
147                // NOTE: currently no such case
148                // Batch sending case
149            } else {
150                NotificationItem item = new NotificationItem();
151                item.id = id;
152                item.direction = dir;
153                if (item.direction == BluetoothShare.DIRECTION_OUTBOUND) {
154                    item.description = mContext.getString(R.string.notification_sending, fileName);
155                } else if (item.direction == BluetoothShare.DIRECTION_INBOUND) {
156                    item.description = mContext
157                            .getString(R.string.notification_receiving, fileName);
158                } else {
159                    if (Constants.LOGVV) {
160                        Log.v(TAG, "mDirection ERROR!");
161                    }
162                }
163                item.totalCurrent = current;
164                item.totalTotal = total;
165
166                mNotifications.put(batchID, item);
167
168                if (Constants.LOGVV) {
169                    Log.v(TAG, "ID=" + item.id + "; batchID=" + batchID + "; totoalCurrent"
170                            + item.totalCurrent + "; totalTotal=" + item.totalTotal);
171                }
172            }
173        }
174        cursor.close();
175
176        // Add the notifications
177        for (NotificationItem item : mNotifications.values()) {
178            // Build the RemoteView object
179            RemoteViews expandedView = new RemoteViews(Constants.THIS_PACKAGE_NAME,
180                    R.layout.status_bar_ongoing_event_progress_bar);
181
182            expandedView.setTextViewText(R.id.description, item.description);
183
184            expandedView.setProgressBar(R.id.progress_bar, item.totalTotal, item.totalCurrent,
185                    item.totalTotal == -1);
186
187            expandedView.setTextViewText(R.id.progress_text, BluetoothOppUtility
188                    .formatProgressText(item.totalTotal, item.totalCurrent));
189
190            // Build the notification object
191            Notification n = new Notification();
192            if (item.direction == BluetoothShare.DIRECTION_OUTBOUND) {
193                n.icon = android.R.drawable.stat_sys_upload;
194                expandedView.setImageViewResource(R.id.appIcon, android.R.drawable.stat_sys_upload);
195            } else if (item.direction == BluetoothShare.DIRECTION_INBOUND) {
196                n.icon = android.R.drawable.stat_sys_download;
197                expandedView.setImageViewResource(R.id.appIcon,
198                        android.R.drawable.stat_sys_download);
199            } else {
200                if (Constants.LOGVV) {
201                    Log.v(TAG, "mDirection ERROR!");
202                }
203            }
204
205            n.flags |= Notification.FLAG_ONGOING_EVENT;
206            n.contentView = expandedView;
207
208            Intent intent = new Intent(Constants.ACTION_LIST);
209            intent.setClassName(Constants.THIS_PACKAGE_NAME, BluetoothOppReceiver.class.getName());
210            intent.setData(Uri.parse(BluetoothShare.CONTENT_URI + "/" + item.id));
211
212            n.contentIntent = PendingIntent.getBroadcast(mContext, 0, intent, 0);
213            mNotificationMgr.notify(item.id, n);
214        }
215    }
216
217    private void updateCompletedNotification() {
218        Cursor cursor = mContext.getContentResolver().query(BluetoothShare.CONTENT_URI, null,
219                WHERE_COMPLETED, null, BluetoothShare._ID);
220        if (cursor == null) {
221            return;
222        }
223
224        for (cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) {
225            // Add the notifications
226            int timeStamp = cursor.getInt(cursor.getColumnIndexOrThrow(BluetoothShare.TIMESTAMP));
227            int dir = cursor.getInt(cursor.getColumnIndexOrThrow(BluetoothShare.DIRECTION));
228            int id = cursor.getInt(cursor.getColumnIndexOrThrow(BluetoothShare._ID));
229            int status = cursor.getInt(cursor.getColumnIndexOrThrow(BluetoothShare.STATUS));
230
231            String fileName = cursor.getString(cursor
232                    .getColumnIndexOrThrow(BluetoothShare.FILENAME_HINT));
233            if (fileName == null) {
234                fileName = mContext.getString(R.string.unknown_file);
235            }
236
237            String title;
238            String caption;
239            Uri contentUri = Uri.parse(BluetoothShare.CONTENT_URI + "/" + id);
240
241            Notification n = new Notification();
242            if (BluetoothShare.isStatusError(status)) {
243                if (dir == BluetoothShare.DIRECTION_OUTBOUND) {
244                    title = mContext.getString(R.string.notification_sent_fail, fileName);
245                } else {
246                    title = mContext.getString(R.string.notification_received_fail, fileName);
247                }
248                caption = mContext.getString(R.string.download_fail_line3, BluetoothOppUtility
249                        .getStatusDescription(mContext, status));
250                n.icon = android.R.drawable.stat_notify_error;
251            } else {
252                if (dir == BluetoothShare.DIRECTION_OUTBOUND) {
253                    title = mContext.getString(R.string.notification_sent, fileName);
254                    n.icon = android.R.drawable.stat_sys_upload_done;
255                } else {
256                    title = mContext.getString(R.string.notification_received, fileName);
257                    n.icon = android.R.drawable.stat_sys_download_done;
258                }
259                caption = mContext.getString(R.string.notification_sent_complete);
260            }
261            Intent intent = new Intent(Constants.ACTION_OPEN);
262            intent.setClassName(Constants.THIS_PACKAGE_NAME, BluetoothOppReceiver.class.getName());
263            intent.setData(contentUri);
264
265            n.setLatestEventInfo(mContext, title, caption, PendingIntent.getBroadcast(mContext, 0,
266                    intent, 0));
267
268            intent = new Intent(Constants.ACTION_HIDE);
269            intent.setClassName(Constants.THIS_PACKAGE_NAME, BluetoothOppReceiver.class.getName());
270            intent.setData(contentUri);
271            n.deleteIntent = PendingIntent.getBroadcast(mContext, 0, intent, 0);
272
273            n.when = timeStamp;
274
275            mNotificationMgr.notify(id, n);
276        }
277        cursor.close();
278    }
279
280    private void updateIncomingFileConfirmNotification() {
281        Cursor cursor = mContext.getContentResolver().query(BluetoothShare.CONTENT_URI, null,
282                WHERE_CONFIRM_PENDING, null, BluetoothShare._ID);
283
284        if (cursor == null) {
285            return;
286        }
287
288        for (cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) {
289            String title = mContext.getString(R.string.incoming_file_confirm_Notification_title);
290            String caption = mContext
291                    .getString(R.string.incoming_file_confirm_Notification_caption);
292            int id = cursor.getInt(cursor.getColumnIndexOrThrow(BluetoothShare._ID));
293            int timeStamp = cursor.getInt(cursor.getColumnIndexOrThrow(BluetoothShare.TIMESTAMP));
294            Uri contentUri = Uri.parse(BluetoothShare.CONTENT_URI + "/" + id);
295
296            Notification n = new Notification();
297            n.icon = R.drawable.bt_incomming_file_notification;
298            n.flags |= Notification.FLAG_ONLY_ALERT_ONCE;
299            n.defaults = Notification.DEFAULT_SOUND;
300            n.tickerText = title;
301            Intent intent = new Intent(Constants.ACTION_INCOMING_FILE_CONFIRM);
302            intent.setClassName(Constants.THIS_PACKAGE_NAME, BluetoothOppReceiver.class.getName());
303            intent.setData(contentUri);
304            n.setLatestEventInfo(mContext, title, caption, PendingIntent.getBroadcast(mContext, 0,
305                    intent, 0));
306
307            intent = new Intent(Constants.ACTION_HIDE);
308            intent.setClassName(Constants.THIS_PACKAGE_NAME, BluetoothOppReceiver.class.getName());
309            intent.setData(contentUri);
310            n.deleteIntent = PendingIntent.getBroadcast(mContext, 0, intent, 0);
311
312            n.when = timeStamp;
313            mNotificationMgr.notify(id, n);
314        }
315        cursor.close();
316    }
317}
318