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