BluetoothOppNotification.java revision 4f6e5cabf9757f362fdfbef8fca30e80523fbf45
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.text.format.Formatter;
45import android.util.Log;
46import android.os.Handler;
47import android.os.Message;
48import android.os.Process;
49
50import java.util.HashMap;
51
52/**
53 * This class handles the updating of the Notification Manager for the cases
54 * where there is an ongoing transfer, incoming transfer need confirm and
55 * complete (successful or failed) transfer.
56 */
57class BluetoothOppNotification {
58    private static final String TAG = "BluetoothOppNotification";
59    private static final boolean V = Constants.VERBOSE;
60
61    static final String status = "(" + BluetoothShare.STATUS + " == '192'" + ")";
62
63    static final String visible = "(" + BluetoothShare.VISIBILITY + " IS NULL OR "
64            + BluetoothShare.VISIBILITY + " == '" + BluetoothShare.VISIBILITY_VISIBLE + "'" + ")";
65
66    static final String confirm = "(" + BluetoothShare.USER_CONFIRMATION + " == '"
67            + BluetoothShare.USER_CONFIRMATION_CONFIRMED + "' OR "
68            + BluetoothShare.USER_CONFIRMATION + " == '"
69            + BluetoothShare.USER_CONFIRMATION_AUTO_CONFIRMED  + "' OR "
70            + BluetoothShare.USER_CONFIRMATION + " == '"
71            + BluetoothShare.USER_CONFIRMATION_HANDOVER_CONFIRMED + "'" + ")";
72
73    static final String not_through_handover = "(" + BluetoothShare.USER_CONFIRMATION + " != '"
74            + BluetoothShare.USER_CONFIRMATION_HANDOVER_CONFIRMED + "'" + ")";
75
76    static final String WHERE_RUNNING = status + " AND " + visible + " AND " + confirm;
77
78    static final String WHERE_COMPLETED = BluetoothShare.STATUS + " >= '200' AND " + visible +
79            " AND " + not_through_handover; // Don't show handover-initiated transfers
80
81    private static final String WHERE_COMPLETED_OUTBOUND = WHERE_COMPLETED + " AND " + "("
82            + BluetoothShare.DIRECTION + " == " + BluetoothShare.DIRECTION_OUTBOUND + ")";
83
84    private static final String WHERE_COMPLETED_INBOUND = WHERE_COMPLETED + " AND " + "("
85            + BluetoothShare.DIRECTION + " == " + BluetoothShare.DIRECTION_INBOUND + ")";
86
87    static final String WHERE_CONFIRM_PENDING = BluetoothShare.USER_CONFIRMATION + " == '"
88            + BluetoothShare.USER_CONFIRMATION_PENDING + "'" + " AND " + visible;
89
90    public NotificationManager mNotificationMgr;
91
92    private Context mContext;
93
94    private HashMap<String, NotificationItem> mNotifications;
95
96    private NotificationUpdateThread mUpdateNotificationThread;
97
98    private int mPendingUpdate = 0;
99
100    private static final int NOTIFICATION_ID_OUTBOUND = -1000005;
101
102    private static final int NOTIFICATION_ID_INBOUND = -1000006;
103
104    private boolean mUpdateCompleteNotification = true;
105
106    private int mActiveNotificationId = 0;
107
108    /**
109     * This inner class is used to describe some properties for one transfer.
110     */
111    static class NotificationItem {
112        int id; // This first field _id in db;
113
114        int direction; // to indicate sending or receiving
115
116        long totalCurrent = 0; // current transfer bytes
117
118        long totalTotal = 0; // total bytes for current transfer
119
120        long timeStamp = 0; // Database time stamp. Used for sorting ongoing transfers.
121
122        String description; // the text above progress bar
123
124        boolean handoverInitiated = false; // transfer initiated by connection handover (eg NFC)
125
126        String destination; // destination associated with this transfer
127    }
128
129    /**
130     * Constructor
131     *
132     * @param ctx The context to use to obtain access to the Notification
133     *            Service
134     */
135    BluetoothOppNotification(Context ctx) {
136        mContext = ctx;
137        mNotificationMgr = (NotificationManager)mContext
138                .getSystemService(Context.NOTIFICATION_SERVICE);
139        mNotifications = new HashMap<String, NotificationItem>();
140    }
141
142    /**
143     * Update the notification ui.
144     */
145    public void updateNotification() {
146        synchronized (BluetoothOppNotification.this) {
147            mPendingUpdate++;
148            if (mPendingUpdate > 1) {
149                if (V) Log.v(TAG, "update too frequent, put in queue");
150                return;
151            }
152            if (!mHandler.hasMessages(NOTIFY)) {
153                if (V) Log.v(TAG, "send message");
154                mHandler.sendMessage(mHandler.obtainMessage(NOTIFY));
155            }
156        }
157    }
158
159    private static final int NOTIFY = 0;
160    // Use 1 second timer to limit notification frequency.
161    // 1. On the first notification, create the update thread.
162    //    Buffer other updates.
163    // 2. Update thread will clear mPendingUpdate.
164    // 3. Handler sends a delayed message to self
165    // 4. Handler checks if there are any more updates after 1 second.
166    // 5. If there is an update, update it else stop.
167    private Handler mHandler = new Handler() {
168        public void handleMessage(Message msg) {
169            switch (msg.what) {
170                case NOTIFY:
171                    synchronized (BluetoothOppNotification.this) {
172                        if (mPendingUpdate > 0 && mUpdateNotificationThread == null) {
173                            if (V) Log.v(TAG, "new notify threadi!");
174                            mUpdateNotificationThread = new NotificationUpdateThread();
175                            mUpdateNotificationThread.start();
176                            if (V) Log.v(TAG, "send delay message");
177                            mHandler.sendMessageDelayed(mHandler.obtainMessage(NOTIFY), 1000);
178                        } else if (mPendingUpdate > 0) {
179                            if (V) Log.v(TAG, "previous thread is not finished yet");
180                            mHandler.sendMessageDelayed(mHandler.obtainMessage(NOTIFY), 1000);
181                        }
182                        break;
183                    }
184              }
185         }
186    };
187
188    private class NotificationUpdateThread extends Thread {
189
190        public NotificationUpdateThread() {
191            super("Notification Update Thread");
192        }
193
194        @Override
195        public void run() {
196            Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
197            synchronized (BluetoothOppNotification.this) {
198                if (mUpdateNotificationThread != this) {
199                    throw new IllegalStateException(
200                            "multiple UpdateThreads in BluetoothOppNotification");
201                }
202                mPendingUpdate = 0;
203            }
204            updateActiveNotification();
205            updateCompletedNotification();
206            updateIncomingFileConfirmNotification();
207            synchronized (BluetoothOppNotification.this) {
208                mUpdateNotificationThread = null;
209            }
210        }
211    }
212
213    private void updateActiveNotification() {
214        // Active transfers
215        Cursor cursor = mContext.getContentResolver().query(BluetoothShare.CONTENT_URI, null,
216                WHERE_RUNNING, null, BluetoothShare._ID);
217        if (cursor == null) {
218            return;
219        }
220
221        // If there is active transfers, then no need to update completed transfer
222        // notifications
223        if (cursor.getCount() > 0) {
224            mUpdateCompleteNotification = false;
225        } else {
226            mUpdateCompleteNotification = true;
227        }
228        if (V) Log.v(TAG, "mUpdateCompleteNotification = " + mUpdateCompleteNotification);
229
230        // Collate the notifications
231        final int timestampIndex = cursor.getColumnIndexOrThrow(BluetoothShare.TIMESTAMP);
232        final int directionIndex = cursor.getColumnIndexOrThrow(BluetoothShare.DIRECTION);
233        final int idIndex = cursor.getColumnIndexOrThrow(BluetoothShare._ID);
234        final int totalBytesIndex = cursor.getColumnIndexOrThrow(BluetoothShare.TOTAL_BYTES);
235        final int currentBytesIndex = cursor.getColumnIndexOrThrow(BluetoothShare.CURRENT_BYTES);
236        final int dataIndex = cursor.getColumnIndexOrThrow(BluetoothShare._DATA);
237        final int filenameHintIndex = cursor.getColumnIndexOrThrow(BluetoothShare.FILENAME_HINT);
238        final int confirmIndex = cursor.getColumnIndexOrThrow(BluetoothShare.USER_CONFIRMATION);
239        final int destinationIndex = cursor.getColumnIndexOrThrow(BluetoothShare.DESTINATION);
240
241        mNotifications.clear();
242        for (cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) {
243            long timeStamp = cursor.getLong(timestampIndex);
244            int dir = cursor.getInt(directionIndex);
245            int id = cursor.getInt(idIndex);
246            long total = cursor.getLong(totalBytesIndex);
247            long current = cursor.getLong(currentBytesIndex);
248            int confirmation = cursor.getInt(confirmIndex);
249
250            String destination = cursor.getString(destinationIndex);
251            String fileName = cursor.getString(dataIndex);
252            if (fileName == null) {
253                fileName = cursor.getString(filenameHintIndex);
254            }
255            if (fileName == null) {
256                fileName = mContext.getString(R.string.unknown_file);
257            }
258
259            String batchID = Long.toString(timeStamp);
260
261            // sending objects in one batch has same timeStamp
262            if (mNotifications.containsKey(batchID)) {
263                // NOTE: currently no such case
264                // Batch sending case
265            } else {
266                NotificationItem item = new NotificationItem();
267                item.timeStamp = timeStamp;
268                item.id = id;
269                item.direction = dir;
270                if (item.direction == BluetoothShare.DIRECTION_OUTBOUND) {
271                    item.description = mContext.getString(R.string.notification_sending, fileName);
272                } else if (item.direction == BluetoothShare.DIRECTION_INBOUND) {
273                    item.description = mContext
274                            .getString(R.string.notification_receiving, fileName);
275                } else {
276                    if (V) Log.v(TAG, "mDirection ERROR!");
277                }
278                item.totalCurrent = current;
279                item.totalTotal = total;
280                item.handoverInitiated =
281                        confirmation == BluetoothShare.USER_CONFIRMATION_HANDOVER_CONFIRMED;
282                item.destination = destination;
283                mNotifications.put(batchID, item);
284
285                if (V) Log.v(TAG, "ID=" + item.id + "; batchID=" + batchID + "; totoalCurrent"
286                            + item.totalCurrent + "; totalTotal=" + item.totalTotal);
287            }
288        }
289        cursor.close();
290
291        // Add the notifications
292        for (NotificationItem item : mNotifications.values()) {
293            if (item.handoverInitiated) {
294                float progress = 0;
295                if (item.totalTotal == -1) {
296                    progress = -1;
297                } else {
298                    progress = (float)item.totalCurrent / item.totalTotal;
299                }
300
301                // Let NFC service deal with notifications for this transfer
302                Intent intent = new Intent(Constants.ACTION_BT_OPP_TRANSFER_PROGRESS);
303                if (item.direction == BluetoothShare.DIRECTION_INBOUND) {
304                    intent.putExtra(Constants.EXTRA_BT_OPP_TRANSFER_DIRECTION,
305                            Constants.DIRECTION_BLUETOOTH_INCOMING);
306                } else {
307                    intent.putExtra(Constants.EXTRA_BT_OPP_TRANSFER_DIRECTION,
308                            Constants.DIRECTION_BLUETOOTH_OUTGOING);
309                }
310                intent.putExtra(Constants.EXTRA_BT_OPP_TRANSFER_ID, item.id);
311                intent.putExtra(Constants.EXTRA_BT_OPP_TRANSFER_PROGRESS, progress);
312                intent.putExtra(Constants.EXTRA_BT_OPP_ADDRESS, item.destination);
313                mContext.sendBroadcast(intent, Constants.HANDOVER_STATUS_PERMISSION);
314                continue;
315            }
316            // Build the notification object
317            // TODO: split description into two rows with filename in second row
318            Notification.Builder b = new Notification.Builder(mContext);
319            b.setColor(mContext.getResources().getColor(
320                    com.android.internal.R.color.system_notification_accent_color));
321            b.setContentTitle(item.description);
322            b.setContentInfo(
323                BluetoothOppUtility.formatProgressText(item.totalTotal, item.totalCurrent));
324            if (item.totalTotal != 0) {
325                if (V) Log.v(TAG, "mCurrentBytes: " + item.totalCurrent +
326                    " mTotalBytes: " + item.totalTotal + " (" +
327                    (int)((item.totalCurrent * 100) / item.totalTotal) + " %)");
328                b.setProgress(100, (int)((item.totalCurrent * 100) / item.totalTotal),
329                    item.totalTotal == -1);
330            } else {
331                b.setProgress(100, 100, item.totalTotal == -1);
332            }
333            b.setWhen(item.timeStamp);
334            if (item.direction == BluetoothShare.DIRECTION_OUTBOUND) {
335                b.setSmallIcon(android.R.drawable.stat_sys_upload);
336            } else if (item.direction == BluetoothShare.DIRECTION_INBOUND) {
337                b.setSmallIcon(android.R.drawable.stat_sys_download);
338            } else {
339                if (V) Log.v(TAG, "mDirection ERROR!");
340            }
341            b.setOngoing(true);
342
343            Intent intent = new Intent(Constants.ACTION_LIST);
344            intent.setClassName(Constants.THIS_PACKAGE_NAME, BluetoothOppReceiver.class.getName());
345            intent.setDataAndNormalize(Uri.parse(BluetoothShare.CONTENT_URI + "/" + item.id));
346
347            b.setContentIntent(PendingIntent.getBroadcast(mContext, 0, intent, 0));
348            mNotificationMgr.notify(item.id, b.getNotification());
349
350            mActiveNotificationId = item.id;
351        }
352    }
353
354    private void updateCompletedNotification() {
355        long timeStamp = 0;
356        int outboundSuccNumber = 0;
357        int outboundFailNumber = 0;
358        int outboundNum;
359        int inboundNum;
360        int inboundSuccNumber = 0;
361        int inboundFailNumber = 0;
362
363        // If there is active transfer, no need to update complete transfer
364        // notification
365        if (!mUpdateCompleteNotification) {
366            if (V) Log.v(TAG, "No need to update complete notification");
367            return;
368        }
369
370        // After merge complete notifications to 2 notifications, there is no
371        // chance to update the active notifications to complete notifications
372        // as before. So need cancel the active notification after the active
373        // transfer becomes complete.
374        if (mNotificationMgr != null && mActiveNotificationId != 0) {
375            mNotificationMgr.cancel(mActiveNotificationId);
376            if (V) Log.v(TAG, "ongoing transfer notification was removed");
377        }
378
379        // Creating outbound notification
380        Cursor cursor = mContext.getContentResolver().query(BluetoothShare.CONTENT_URI, null,
381                WHERE_COMPLETED_OUTBOUND, null, BluetoothShare.TIMESTAMP + " DESC");
382        if (cursor == null) {
383            return;
384        }
385
386        final int timestampIndex = cursor.getColumnIndexOrThrow(BluetoothShare.TIMESTAMP);
387        final int statusIndex = cursor.getColumnIndexOrThrow(BluetoothShare.STATUS);
388
389        for (cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) {
390            if (cursor.isFirst()) {
391                // Display the time for the latest transfer
392                timeStamp = cursor.getLong(timestampIndex);
393            }
394            int status = cursor.getInt(statusIndex);
395
396            if (BluetoothShare.isStatusError(status)) {
397                outboundFailNumber++;
398            } else {
399                outboundSuccNumber++;
400            }
401        }
402        if (V) Log.v(TAG, "outbound: succ-" + outboundSuccNumber + "  fail-" + outboundFailNumber);
403        cursor.close();
404
405        outboundNum = outboundSuccNumber + outboundFailNumber;
406        // create the outbound notification
407        if (outboundNum > 0) {
408            String unsuccess_caption = mContext.getResources().getQuantityString(
409                    R.plurals.noti_caption_unsuccessful, outboundFailNumber, outboundFailNumber);
410            String caption = mContext.getResources().getQuantityString(
411                    R.plurals.noti_caption_success, outboundSuccNumber, outboundSuccNumber,
412                    unsuccess_caption);
413            Intent content_intent = new Intent(Constants.ACTION_OPEN_OUTBOUND_TRANSFER)
414                    .setClassName(Constants.THIS_PACKAGE_NAME,
415                            BluetoothOppReceiver.class.getName());
416            Intent delete_intent = new Intent(Constants.ACTION_COMPLETE_HIDE)
417                    .setClassName(Constants.THIS_PACKAGE_NAME,
418                            BluetoothOppReceiver.class.getName());
419            Notification outNoti = new Notification.Builder(mContext)
420                    .setContentTitle(mContext.getString(R.string.outbound_noti_title))
421                    .setContentText(caption)
422                    .setSmallIcon(android.R.drawable.stat_sys_upload_done)
423                    .setColor(mContext.getResources().getColor(
424                            com.android.internal.R.color.system_notification_accent_color,
425                            mContext.getTheme()))
426                    .setContentIntent(PendingIntent.getBroadcast(mContext, 0, content_intent, 0))
427                    .setDeleteIntent(PendingIntent.getBroadcast(mContext, 0, delete_intent, 0))
428                    .setWhen(timeStamp)
429                    .build();
430            mNotificationMgr.notify(NOTIFICATION_ID_OUTBOUND, outNoti);
431        } else {
432            if (mNotificationMgr != null) {
433                mNotificationMgr.cancel(NOTIFICATION_ID_OUTBOUND);
434                if (V) Log.v(TAG, "outbound notification was removed.");
435            }
436        }
437
438        // Creating inbound notification
439        cursor = mContext.getContentResolver().query(BluetoothShare.CONTENT_URI, null,
440                WHERE_COMPLETED_INBOUND, null, BluetoothShare.TIMESTAMP + " DESC");
441        if (cursor == null) {
442            return;
443        }
444
445        for (cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) {
446            if (cursor.isFirst()) {
447                // Display the time for the latest transfer
448                timeStamp = cursor.getLong(timestampIndex);
449            }
450            int status = cursor.getInt(statusIndex);
451
452            if (BluetoothShare.isStatusError(status)) {
453                inboundFailNumber++;
454            } else {
455                inboundSuccNumber++;
456            }
457        }
458        if (V) Log.v(TAG, "inbound: succ-" + inboundSuccNumber + "  fail-" + inboundFailNumber);
459        cursor.close();
460
461        inboundNum = inboundSuccNumber + inboundFailNumber;
462        // create the inbound notification
463        if (inboundNum > 0) {
464            String unsuccess_caption = mContext.getResources().getQuantityString(
465                    R.plurals.noti_caption_unsuccessful, inboundFailNumber, inboundFailNumber);
466            String caption = mContext.getResources().getQuantityString(
467                    R.plurals.noti_caption_success, inboundSuccNumber, inboundSuccNumber,
468                    unsuccess_caption);
469            Intent content_intent = new Intent(Constants.ACTION_OPEN_INBOUND_TRANSFER)
470                    .setClassName(Constants.THIS_PACKAGE_NAME,
471                            BluetoothOppReceiver.class.getName());
472            Intent delete_intent = new Intent(Constants.ACTION_COMPLETE_HIDE)
473                    .setClassName(Constants.THIS_PACKAGE_NAME,
474                            BluetoothOppReceiver.class.getName());
475            Notification inNoti = new Notification.Builder(mContext)
476                    .setContentTitle(mContext.getString(R.string.inbound_noti_title))
477                    .setContentText(caption)
478                    .setSmallIcon(android.R.drawable.stat_sys_download_done)
479                    .setColor(mContext.getResources().getColor(
480                            com.android.internal.R.color.system_notification_accent_color,
481                            mContext.getTheme()))
482                    .setContentIntent(PendingIntent.getBroadcast(mContext, 0, content_intent, 0))
483                    .setDeleteIntent(PendingIntent.getBroadcast(mContext, 0, delete_intent, 0))
484                    .setWhen(timeStamp)
485                    .build();
486            mNotificationMgr.notify(NOTIFICATION_ID_INBOUND, inNoti);
487        } else {
488            if (mNotificationMgr != null) {
489                mNotificationMgr.cancel(NOTIFICATION_ID_INBOUND);
490                if (V) Log.v(TAG, "inbound notification was removed.");
491            }
492        }
493    }
494
495    private void updateIncomingFileConfirmNotification() {
496        Cursor cursor = mContext.getContentResolver().query(BluetoothShare.CONTENT_URI, null,
497                WHERE_CONFIRM_PENDING, null, BluetoothShare._ID);
498
499        if (cursor == null) {
500            return;
501        }
502
503        for (cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) {
504          BluetoothOppTransferInfo info = new BluetoothOppTransferInfo();
505          BluetoothOppUtility.fillRecord(mContext, cursor, info);
506          Uri contentUri = Uri.parse(BluetoothShare.CONTENT_URI + "/" + info.mID);
507          Intent baseIntent = new Intent().setDataAndNormalize(contentUri)
508              .setClassName(Constants.THIS_PACKAGE_NAME, BluetoothOppReceiver.class.getName());
509
510          Notification n = new Notification.Builder(mContext)
511              .setOnlyAlertOnce(true)
512              .setOngoing(true)
513              .setVibrate(new long[] { 200 })
514              .setWhen(info.mTimeStamp)
515              .setDefaults(Notification.DEFAULT_SOUND)
516              .setPriority(Notification.PRIORITY_HIGH)
517              .addAction(R.drawable.ic_decline,
518                  mContext.getText(R.string.incoming_file_confirm_cancel),
519                      PendingIntent.getBroadcast(mContext, 0,
520                          new Intent(baseIntent).setAction(Constants.ACTION_DECLINE), 0))
521              .addAction(R.drawable.ic_accept,
522                  mContext.getText(R.string.incoming_file_confirm_ok),
523                      PendingIntent.getBroadcast(mContext, 0,
524                          new Intent(baseIntent).setAction(Constants.ACTION_ACCEPT), 0))
525              .setContentIntent(PendingIntent.getBroadcast(mContext, 0,
526                  new Intent(baseIntent).setAction(Constants.ACTION_INCOMING_FILE_CONFIRM), 0))
527              .setDeleteIntent(PendingIntent.getBroadcast(mContext, 0,
528                  new Intent(baseIntent).setAction(Constants.ACTION_HIDE), 0))
529              .setColor(mContext.getResources().getColor(
530                  com.android.internal.R.color.system_notification_accent_color))
531              .setContentTitle(mContext.getText(R.string.incoming_file_confirm_Notification_title))
532              .setContentText(info.mFileName)
533              .setStyle(new Notification.BigTextStyle().bigText(mContext.getString(
534                  R.string.incoming_file_confirm_Notification_content,
535                  info.mDeviceName, info.mFileName)))
536              .setContentInfo(Formatter.formatFileSize(mContext, info.mTotalBytes))
537              .setSmallIcon(R.drawable.bt_incomming_file_notification)
538              .build();
539          mNotificationMgr.notify(info.mID, n);
540        }
541        cursor.close();
542    }
543}
544