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 
33 package com.android.bluetooth.opp;
34 
35 import com.android.bluetooth.R;
36 
37 import android.content.Context;
38 import android.app.Notification;
39 import android.app.NotificationManager;
40 import android.app.PendingIntent;
41 import android.content.Intent;
42 import android.database.Cursor;
43 import android.net.Uri;
44 import android.text.format.Formatter;
45 import android.util.Log;
46 import android.os.Handler;
47 import android.os.Message;
48 import android.os.Process;
49 
50 import 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  */
57 class 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      */
BluetoothOppNotification(Context ctx)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      */
updateNotification()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 
NotificationUpdateThread()190         public NotificationUpdateThread() {
191             super("Notification Update Thread");
192         }
193 
194         @Override
run()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 
updateActiveNotification()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 
updateCompletedNotification()354     private void updateCompletedNotification() {
355         String title;
356         String unsuccess_caption;
357         String caption;
358         long timeStamp = 0;
359         int outboundSuccNumber = 0;
360         int outboundFailNumber = 0;
361         int outboundNum;
362         int inboundNum;
363         int inboundSuccNumber = 0;
364         int inboundFailNumber = 0;
365         Intent intent;
366 
367         // If there is active transfer, no need to update complete transfer
368         // notification
369         if (!mUpdateCompleteNotification) {
370             if (V) Log.v(TAG, "No need to update complete notification");
371             return;
372         }
373 
374         // After merge complete notifications to 2 notifications, there is no
375         // chance to update the active notifications to complete notifications
376         // as before. So need cancel the active notification after the active
377         // transfer becomes complete.
378         if (mNotificationMgr != null && mActiveNotificationId != 0) {
379             mNotificationMgr.cancel(mActiveNotificationId);
380             if (V) Log.v(TAG, "ongoing transfer notification was removed");
381         }
382 
383         // Creating outbound notification
384         Cursor cursor = mContext.getContentResolver().query(BluetoothShare.CONTENT_URI, null,
385                 WHERE_COMPLETED_OUTBOUND, null, BluetoothShare.TIMESTAMP + " DESC");
386         if (cursor == null) {
387             return;
388         }
389 
390         final int timestampIndex = cursor.getColumnIndexOrThrow(BluetoothShare.TIMESTAMP);
391         final int statusIndex = cursor.getColumnIndexOrThrow(BluetoothShare.STATUS);
392 
393         for (cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) {
394             if (cursor.isFirst()) {
395                 // Display the time for the latest transfer
396                 timeStamp = cursor.getLong(timestampIndex);
397             }
398             int status = cursor.getInt(statusIndex);
399 
400             if (BluetoothShare.isStatusError(status)) {
401                 outboundFailNumber++;
402             } else {
403                 outboundSuccNumber++;
404             }
405         }
406         if (V) Log.v(TAG, "outbound: succ-" + outboundSuccNumber + "  fail-" + outboundFailNumber);
407         cursor.close();
408 
409         outboundNum = outboundSuccNumber + outboundFailNumber;
410         // create the outbound notification
411         if (outboundNum > 0) {
412             Notification outNoti = new Notification();
413             outNoti.icon = android.R.drawable.stat_sys_upload_done;
414             title = mContext.getString(R.string.outbound_noti_title);
415             unsuccess_caption = mContext.getResources().getQuantityString(
416                     R.plurals.noti_caption_unsuccessful, outboundFailNumber, outboundFailNumber);
417             caption = mContext.getResources().getQuantityString(
418                     R.plurals.noti_caption_success, outboundSuccNumber, outboundSuccNumber,
419                     unsuccess_caption);
420             intent = new Intent(Constants.ACTION_OPEN_OUTBOUND_TRANSFER);
421             intent.setClassName(Constants.THIS_PACKAGE_NAME, BluetoothOppReceiver.class.getName());
422             outNoti.color = mContext.getResources().getColor(
423                     com.android.internal.R.color.system_notification_accent_color);
424             outNoti.setLatestEventInfo(mContext, title, caption, PendingIntent.getBroadcast(
425                     mContext, 0, intent, 0));
426             intent = new Intent(Constants.ACTION_COMPLETE_HIDE);
427             intent.setClassName(Constants.THIS_PACKAGE_NAME, BluetoothOppReceiver.class.getName());
428             outNoti.deleteIntent = PendingIntent.getBroadcast(mContext, 0, intent, 0);
429             outNoti.when = timeStamp;
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             Notification inNoti = new Notification();
465             inNoti.icon = android.R.drawable.stat_sys_download_done;
466             title = mContext.getString(R.string.inbound_noti_title);
467             unsuccess_caption = mContext.getResources().getQuantityString(
468                     R.plurals.noti_caption_unsuccessful, inboundFailNumber, inboundFailNumber);
469             caption = mContext.getResources().getQuantityString(
470                     R.plurals.noti_caption_success, inboundSuccNumber, inboundSuccNumber,
471                     unsuccess_caption);
472             intent = new Intent(Constants.ACTION_OPEN_INBOUND_TRANSFER);
473             intent.setClassName(Constants.THIS_PACKAGE_NAME, BluetoothOppReceiver.class.getName());
474             inNoti.color = mContext.getResources().getColor(
475                     com.android.internal.R.color.system_notification_accent_color);
476             inNoti.setLatestEventInfo(mContext, title, caption, PendingIntent.getBroadcast(
477                     mContext, 0, intent, 0));
478             intent = new Intent(Constants.ACTION_COMPLETE_HIDE);
479             intent.setClassName(Constants.THIS_PACKAGE_NAME, BluetoothOppReceiver.class.getName());
480             inNoti.deleteIntent = PendingIntent.getBroadcast(mContext, 0, intent, 0);
481             inNoti.when = timeStamp;
482             mNotificationMgr.notify(NOTIFICATION_ID_INBOUND, inNoti);
483         } else {
484             if (mNotificationMgr != null) {
485                 mNotificationMgr.cancel(NOTIFICATION_ID_INBOUND);
486                 if (V) Log.v(TAG, "inbound notification was removed.");
487             }
488         }
489     }
490 
updateIncomingFileConfirmNotification()491     private void updateIncomingFileConfirmNotification() {
492         Cursor cursor = mContext.getContentResolver().query(BluetoothShare.CONTENT_URI, null,
493                 WHERE_CONFIRM_PENDING, null, BluetoothShare._ID);
494 
495         if (cursor == null) {
496             return;
497         }
498 
499         for (cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) {
500           BluetoothOppTransferInfo info = new BluetoothOppTransferInfo();
501           BluetoothOppUtility.fillRecord(mContext, cursor, info);
502           Uri contentUri = Uri.parse(BluetoothShare.CONTENT_URI + "/" + info.mID);
503           Intent baseIntent = new Intent().setDataAndNormalize(contentUri)
504               .setClassName(Constants.THIS_PACKAGE_NAME, BluetoothOppReceiver.class.getName());
505 
506           Notification n = new Notification.Builder(mContext)
507               .setOnlyAlertOnce(true)
508               .setOngoing(true)
509               .setVibrate(new long[] { 200 })
510               .setWhen(info.mTimeStamp)
511               .setDefaults(Notification.DEFAULT_SOUND)
512               .setPriority(Notification.PRIORITY_HIGH)
513               .addAction(R.drawable.ic_decline,
514                   mContext.getText(R.string.incoming_file_confirm_cancel),
515                       PendingIntent.getBroadcast(mContext, 0,
516                           new Intent(baseIntent).setAction(Constants.ACTION_DECLINE), 0))
517               .addAction(R.drawable.ic_accept,
518                   mContext.getText(R.string.incoming_file_confirm_ok),
519                       PendingIntent.getBroadcast(mContext, 0,
520                           new Intent(baseIntent).setAction(Constants.ACTION_ACCEPT), 0))
521               .setContentIntent(PendingIntent.getBroadcast(mContext, 0,
522                   new Intent(baseIntent).setAction(Constants.ACTION_INCOMING_FILE_CONFIRM), 0))
523               .setDeleteIntent(PendingIntent.getBroadcast(mContext, 0,
524                   new Intent(baseIntent).setAction(Constants.ACTION_HIDE), 0))
525               .setColor(mContext.getResources().getColor(
526                   com.android.internal.R.color.system_notification_accent_color))
527               .setContentTitle(mContext.getText(R.string.incoming_file_confirm_Notification_title))
528               .setContentText(info.mFileName)
529               .setStyle(new Notification.BigTextStyle().bigText(mContext.getString(
530                   R.string.incoming_file_confirm_Notification_content,
531                   info.mDeviceName, info.mFileName)))
532               .setContentInfo(Formatter.formatFileSize(mContext, info.mTotalBytes))
533               .setSmallIcon(R.drawable.bt_incomming_file_notification)
534               .build();
535           mNotificationMgr.notify(info.mID, n);
536         }
537         cursor.close();
538     }
539 }
540