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