blob: 5e4ec49310ecb19cc4395ef90801614e8e0a9fb0 [file] [log] [blame]
/*
* Copyright (c) 2008-2009, Motorola, Inc.
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* - Neither the name of the Motorola, Inc. nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
package com.android.bluetooth.opp;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.graphics.drawable.Icon;
import android.net.Uri;
import android.os.Handler;
import android.os.Message;
import android.os.Process;
import android.text.format.Formatter;
import android.util.Log;
import com.android.bluetooth.R;
import java.util.HashMap;
/**
* This class handles the updating of the Notification Manager for the cases
* where there is an ongoing transfer, incoming transfer need confirm and
* complete (successful or failed) transfer.
*/
class BluetoothOppNotification {
private static final String TAG = "BluetoothOppNotification";
private static final boolean V = Constants.VERBOSE;
static final String STATUS = "(" + BluetoothShare.STATUS + " == '192'" + ")";
static final String VISIBLE =
"(" + BluetoothShare.VISIBILITY + " IS NULL OR " + BluetoothShare.VISIBILITY + " == '"
+ BluetoothShare.VISIBILITY_VISIBLE + "'" + ")";
static final String CONFIRM = "(" + BluetoothShare.USER_CONFIRMATION + " == '"
+ BluetoothShare.USER_CONFIRMATION_CONFIRMED + "' OR "
+ BluetoothShare.USER_CONFIRMATION + " == '"
+ BluetoothShare.USER_CONFIRMATION_AUTO_CONFIRMED + "' OR "
+ BluetoothShare.USER_CONFIRMATION + " == '"
+ BluetoothShare.USER_CONFIRMATION_HANDOVER_CONFIRMED + "'" + ")";
static final String NOT_THROUGH_HANDOVER = "(" + BluetoothShare.USER_CONFIRMATION + " != '"
+ BluetoothShare.USER_CONFIRMATION_HANDOVER_CONFIRMED + "'" + ")";
static final String WHERE_RUNNING = STATUS + " AND " + VISIBLE + " AND " + CONFIRM;
static final String WHERE_COMPLETED =
BluetoothShare.STATUS + " >= '200' AND " + VISIBLE + " AND " + NOT_THROUGH_HANDOVER;
// Don't show handover-initiated transfers
private static final String WHERE_COMPLETED_OUTBOUND =
WHERE_COMPLETED + " AND " + "(" + BluetoothShare.DIRECTION + " == "
+ BluetoothShare.DIRECTION_OUTBOUND + ")";
private static final String WHERE_COMPLETED_INBOUND =
WHERE_COMPLETED + " AND " + "(" + BluetoothShare.DIRECTION + " == "
+ BluetoothShare.DIRECTION_INBOUND + ")";
static final String WHERE_CONFIRM_PENDING =
BluetoothShare.USER_CONFIRMATION + " == '" + BluetoothShare.USER_CONFIRMATION_PENDING
+ "'" + " AND " + VISIBLE;
public NotificationManager mNotificationMgr;
private NotificationChannel mNotificationChannel;
private static final String OPP_NOTIFICATION_CHANNEL = "opp_notification_channel";
private Context mContext;
private HashMap<String, NotificationItem> mNotifications;
private NotificationUpdateThread mUpdateNotificationThread;
private int mPendingUpdate = 0;
public static final int NOTIFICATION_ID_PROGRESS = -1000004;
private static final int NOTIFICATION_ID_OUTBOUND_COMPLETE = -1000005;
private static final int NOTIFICATION_ID_INBOUND_COMPLETE = -1000006;
private boolean mUpdateCompleteNotification = true;
private ContentResolver mContentResolver = null;
/**
* This inner class is used to describe some properties for one transfer.
*/
static class NotificationItem {
public int id; // This first field _id in db;
public int direction; // to indicate sending or receiving
public long totalCurrent = 0; // current transfer bytes
public long totalTotal = 0; // total bytes for current transfer
public long timeStamp = 0; // Database time stamp. Used for sorting ongoing transfers.
public String description; // the text above progress bar
public boolean handoverInitiated = false;
// transfer initiated by connection handover (eg NFC)
public String destination; // destination associated with this transfer
}
/**
* Constructor
*
* @param ctx The context to use to obtain access to the Notification
* Service
*/
BluetoothOppNotification(Context ctx) {
mContext = ctx;
mNotificationMgr =
(NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
mNotificationChannel = new NotificationChannel(OPP_NOTIFICATION_CHANNEL,
mContext.getString(R.string.opp_notification_group),
NotificationManager.IMPORTANCE_HIGH);
mNotificationMgr.createNotificationChannel(mNotificationChannel);
mNotifications = new HashMap<String, NotificationItem>();
// Get Content Resolver object one time
mContentResolver = mContext.getContentResolver();
}
/**
* Update the notification ui.
*/
public void updateNotification() {
synchronized (BluetoothOppNotification.this) {
mPendingUpdate++;
if (mPendingUpdate > 1) {
if (V) {
Log.v(TAG, "update too frequent, put in queue");
}
return;
}
if (!mHandler.hasMessages(NOTIFY)) {
if (V) {
Log.v(TAG, "send message");
}
mHandler.sendMessage(mHandler.obtainMessage(NOTIFY));
}
}
}
private static final int NOTIFY = 0;
// Use 1 second timer to limit notification frequency.
// 1. On the first notification, create the update thread.
// Buffer other updates.
// 2. Update thread will clear mPendingUpdate.
// 3. Handler sends a delayed message to self
// 4. Handler checks if there are any more updates after 1 second.
// 5. If there is an update, update it else stop.
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case NOTIFY:
synchronized (BluetoothOppNotification.this) {
if (mPendingUpdate > 0 && mUpdateNotificationThread == null) {
if (V) {
Log.v(TAG, "new notify threadi!");
}
mUpdateNotificationThread = new NotificationUpdateThread();
mUpdateNotificationThread.start();
if (V) {
Log.v(TAG, "send delay message");
}
mHandler.sendMessageDelayed(mHandler.obtainMessage(NOTIFY), 1000);
} else if (mPendingUpdate > 0) {
if (V) {
Log.v(TAG, "previous thread is not finished yet");
}
mHandler.sendMessageDelayed(mHandler.obtainMessage(NOTIFY), 1000);
}
break;
}
}
}
};
private class NotificationUpdateThread extends Thread {
NotificationUpdateThread() {
super("Notification Update Thread");
}
@Override
public void run() {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
synchronized (BluetoothOppNotification.this) {
if (mUpdateNotificationThread != this) {
throw new IllegalStateException(
"multiple UpdateThreads in BluetoothOppNotification");
}
mPendingUpdate = 0;
}
updateActiveNotification();
updateCompletedNotification();
updateIncomingFileConfirmNotification();
synchronized (BluetoothOppNotification.this) {
mUpdateNotificationThread = null;
}
}
}
private void updateActiveNotification() {
// Active transfers
Cursor cursor =
mContentResolver.query(BluetoothShare.CONTENT_URI, null, WHERE_RUNNING, null,
BluetoothShare._ID);
if (cursor == null) {
return;
}
// If there is active transfers, then no need to update completed transfer
// notifications
if (cursor.getCount() > 0) {
mUpdateCompleteNotification = false;
} else {
mUpdateCompleteNotification = true;
}
if (V) {
Log.v(TAG, "mUpdateCompleteNotification = " + mUpdateCompleteNotification);
}
// Collate the notifications
final int timestampIndex = cursor.getColumnIndexOrThrow(BluetoothShare.TIMESTAMP);
final int directionIndex = cursor.getColumnIndexOrThrow(BluetoothShare.DIRECTION);
final int idIndex = cursor.getColumnIndexOrThrow(BluetoothShare._ID);
final int totalBytesIndex = cursor.getColumnIndexOrThrow(BluetoothShare.TOTAL_BYTES);
final int currentBytesIndex = cursor.getColumnIndexOrThrow(BluetoothShare.CURRENT_BYTES);
final int dataIndex = cursor.getColumnIndexOrThrow(BluetoothShare._DATA);
final int filenameHintIndex = cursor.getColumnIndexOrThrow(BluetoothShare.FILENAME_HINT);
final int confirmIndex = cursor.getColumnIndexOrThrow(BluetoothShare.USER_CONFIRMATION);
final int destinationIndex = cursor.getColumnIndexOrThrow(BluetoothShare.DESTINATION);
mNotifications.clear();
for (cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) {
long timeStamp = cursor.getLong(timestampIndex);
int dir = cursor.getInt(directionIndex);
int id = cursor.getInt(idIndex);
long total = cursor.getLong(totalBytesIndex);
long current = cursor.getLong(currentBytesIndex);
int confirmation = cursor.getInt(confirmIndex);
String destination = cursor.getString(destinationIndex);
String fileName = cursor.getString(dataIndex);
if (fileName == null) {
fileName = cursor.getString(filenameHintIndex);
}
if (fileName == null) {
fileName = mContext.getString(R.string.unknown_file);
}
String batchID = Long.toString(timeStamp);
// sending objects in one batch has same timeStamp
if (mNotifications.containsKey(batchID)) {
// NOTE: currently no such case
// Batch sending case
} else {
NotificationItem item = new NotificationItem();
item.timeStamp = timeStamp;
item.id = id;
item.direction = dir;
if (item.direction == BluetoothShare.DIRECTION_OUTBOUND) {
item.description = mContext.getString(R.string.notification_sending, fileName);
} else if (item.direction == BluetoothShare.DIRECTION_INBOUND) {
item.description =
mContext.getString(R.string.notification_receiving, fileName);
} else {
if (V) {
Log.v(TAG, "mDirection ERROR!");
}
}
item.totalCurrent = current;
item.totalTotal = total;
item.handoverInitiated =
confirmation == BluetoothShare.USER_CONFIRMATION_HANDOVER_CONFIRMED;
item.destination = destination;
mNotifications.put(batchID, item);
if (V) {
Log.v(TAG, "ID=" + item.id + "; batchID=" + batchID + "; totoalCurrent"
+ item.totalCurrent + "; totalTotal=" + item.totalTotal);
}
}
}
cursor.close();
// Add the notifications
for (NotificationItem item : mNotifications.values()) {
if (item.handoverInitiated) {
float progress = 0;
if (item.totalTotal == -1) {
progress = -1;
} else {
progress = (float) item.totalCurrent / item.totalTotal;
}
// Let NFC service deal with notifications for this transfer
Intent intent = new Intent(Constants.ACTION_BT_OPP_TRANSFER_PROGRESS);
if (item.direction == BluetoothShare.DIRECTION_INBOUND) {
intent.putExtra(Constants.EXTRA_BT_OPP_TRANSFER_DIRECTION,
Constants.DIRECTION_BLUETOOTH_INCOMING);
} else {
intent.putExtra(Constants.EXTRA_BT_OPP_TRANSFER_DIRECTION,
Constants.DIRECTION_BLUETOOTH_OUTGOING);
}
intent.putExtra(Constants.EXTRA_BT_OPP_TRANSFER_ID, item.id);
intent.putExtra(Constants.EXTRA_BT_OPP_TRANSFER_PROGRESS, progress);
intent.putExtra(Constants.EXTRA_BT_OPP_ADDRESS, item.destination);
mContext.sendBroadcast(intent, Constants.HANDOVER_STATUS_PERMISSION);
continue;
}
// Build the notification object
// TODO: split description into two rows with filename in second row
Notification.Builder b = new Notification.Builder(mContext, OPP_NOTIFICATION_CHANNEL);
b.setOnlyAlertOnce(true);
b.setColor(mContext.getResources()
.getColor(com.android.internal.R.color.system_notification_accent_color,
mContext.getTheme()));
b.setContentTitle(item.description);
b.setSubText(
BluetoothOppUtility.formatProgressText(item.totalTotal, item.totalCurrent));
if (item.totalTotal != 0) {
if (V) {
Log.v(TAG, "mCurrentBytes: " + item.totalCurrent + " mTotalBytes: "
+ item.totalTotal + " (" + (int) ((item.totalCurrent * 100)
/ item.totalTotal) + " %)");
}
b.setProgress(100, (int) ((item.totalCurrent * 100) / item.totalTotal),
item.totalTotal == -1);
} else {
b.setProgress(100, 100, item.totalTotal == -1);
}
b.setWhen(item.timeStamp);
if (item.direction == BluetoothShare.DIRECTION_OUTBOUND) {
b.setSmallIcon(android.R.drawable.stat_sys_upload);
} else if (item.direction == BluetoothShare.DIRECTION_INBOUND) {
b.setSmallIcon(android.R.drawable.stat_sys_download);
} else {
if (V) {
Log.v(TAG, "mDirection ERROR!");
}
}
b.setOngoing(true);
b.setLocalOnly(true);
Intent intent = new Intent(Constants.ACTION_LIST);
intent.setClassName(Constants.THIS_PACKAGE_NAME, BluetoothOppReceiver.class.getName());
intent.setDataAndNormalize(Uri.parse(BluetoothShare.CONTENT_URI + "/" + item.id));
b.setContentIntent(PendingIntent.getBroadcast(mContext, 0, intent, 0));
mNotificationMgr.notify(NOTIFICATION_ID_PROGRESS, b.build());
}
}
private void updateCompletedNotification() {
long timeStamp = 0;
int outboundSuccNumber = 0;
int outboundFailNumber = 0;
int outboundNum;
int inboundNum;
int inboundSuccNumber = 0;
int inboundFailNumber = 0;
// Creating outbound notification
Cursor cursor =
mContentResolver.query(BluetoothShare.CONTENT_URI, null, WHERE_COMPLETED_OUTBOUND,
null, BluetoothShare.TIMESTAMP + " DESC");
if (cursor == null) {
return;
}
final int timestampIndex = cursor.getColumnIndexOrThrow(BluetoothShare.TIMESTAMP);
final int statusIndex = cursor.getColumnIndexOrThrow(BluetoothShare.STATUS);
for (cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) {
if (cursor.isFirst()) {
// Display the time for the latest transfer
timeStamp = cursor.getLong(timestampIndex);
}
int status = cursor.getInt(statusIndex);
if (BluetoothShare.isStatusError(status)) {
outboundFailNumber++;
} else {
outboundSuccNumber++;
}
}
if (V) {
Log.v(TAG, "outbound: succ-" + outboundSuccNumber + " fail-" + outboundFailNumber);
}
cursor.close();
outboundNum = outboundSuccNumber + outboundFailNumber;
// create the outbound notification
if (outboundNum > 0) {
String unsuccessCaption = mContext.getResources()
.getQuantityString(R.plurals.noti_caption_unsuccessful, outboundFailNumber,
outboundFailNumber);
String caption = mContext.getResources()
.getQuantityString(R.plurals.noti_caption_success, outboundSuccNumber,
outboundSuccNumber, unsuccessCaption);
Intent contentIntent = new Intent(Constants.ACTION_OPEN_OUTBOUND_TRANSFER).setClassName(
Constants.THIS_PACKAGE_NAME, BluetoothOppReceiver.class.getName());
Intent deleteIntent = new Intent(Constants.ACTION_COMPLETE_HIDE).setClassName(
Constants.THIS_PACKAGE_NAME, BluetoothOppReceiver.class.getName());
Notification outNoti =
new Notification.Builder(mContext, OPP_NOTIFICATION_CHANNEL).setOnlyAlertOnce(
true)
.setContentTitle(mContext.getString(R.string.outbound_noti_title))
.setContentText(caption)
.setSmallIcon(android.R.drawable.stat_sys_upload_done)
.setColor(mContext.getResources()
.getColor(
com.android.internal.R.color
.system_notification_accent_color,
mContext.getTheme()))
.setContentIntent(
PendingIntent.getBroadcast(mContext, 0, contentIntent, 0))
.setDeleteIntent(
PendingIntent.getBroadcast(mContext, 0, deleteIntent, 0))
.setWhen(timeStamp)
.setLocalOnly(true)
.build();
mNotificationMgr.notify(NOTIFICATION_ID_OUTBOUND_COMPLETE, outNoti);
} else {
if (mNotificationMgr != null) {
mNotificationMgr.cancel(NOTIFICATION_ID_OUTBOUND_COMPLETE);
if (V) {
Log.v(TAG, "outbound notification was removed.");
}
}
}
// Creating inbound notification
cursor = mContentResolver.query(BluetoothShare.CONTENT_URI, null, WHERE_COMPLETED_INBOUND,
null, BluetoothShare.TIMESTAMP + " DESC");
if (cursor == null) {
return;
}
for (cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) {
if (cursor.isFirst()) {
// Display the time for the latest transfer
timeStamp = cursor.getLong(timestampIndex);
}
int status = cursor.getInt(statusIndex);
if (BluetoothShare.isStatusError(status)) {
inboundFailNumber++;
} else {
inboundSuccNumber++;
}
}
if (V) {
Log.v(TAG, "inbound: succ-" + inboundSuccNumber + " fail-" + inboundFailNumber);
}
cursor.close();
inboundNum = inboundSuccNumber + inboundFailNumber;
// create the inbound notification
if (inboundNum > 0) {
String unsuccessCaption = mContext.getResources()
.getQuantityString(R.plurals.noti_caption_unsuccessful, inboundFailNumber,
inboundFailNumber);
String caption = mContext.getResources()
.getQuantityString(R.plurals.noti_caption_success, inboundSuccNumber,
inboundSuccNumber, unsuccessCaption);
Intent contentIntent = new Intent(Constants.ACTION_OPEN_INBOUND_TRANSFER).setClassName(
Constants.THIS_PACKAGE_NAME, BluetoothOppReceiver.class.getName());
Intent deleteIntent = new Intent(Constants.ACTION_COMPLETE_HIDE).setClassName(
Constants.THIS_PACKAGE_NAME, BluetoothOppReceiver.class.getName());
Notification inNoti =
new Notification.Builder(mContext, OPP_NOTIFICATION_CHANNEL).setOnlyAlertOnce(
true)
.setContentTitle(mContext.getString(R.string.inbound_noti_title))
.setContentText(caption)
.setSmallIcon(android.R.drawable.stat_sys_download_done)
.setColor(mContext.getResources()
.getColor(
com.android.internal.R.color
.system_notification_accent_color,
mContext.getTheme()))
.setContentIntent(
PendingIntent.getBroadcast(mContext, 0, contentIntent, 0))
.setDeleteIntent(
PendingIntent.getBroadcast(mContext, 0, deleteIntent, 0))
.setWhen(timeStamp)
.setLocalOnly(true)
.build();
mNotificationMgr.notify(NOTIFICATION_ID_INBOUND_COMPLETE, inNoti);
} else {
if (mNotificationMgr != null) {
mNotificationMgr.cancel(NOTIFICATION_ID_INBOUND_COMPLETE);
if (V) {
Log.v(TAG, "inbound notification was removed.");
}
}
}
}
private void updateIncomingFileConfirmNotification() {
Cursor cursor =
mContentResolver.query(BluetoothShare.CONTENT_URI, null, WHERE_CONFIRM_PENDING,
null, BluetoothShare._ID);
if (cursor == null) {
return;
}
for (cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) {
BluetoothOppTransferInfo info = new BluetoothOppTransferInfo();
BluetoothOppUtility.fillRecord(mContext, cursor, info);
Uri contentUri = Uri.parse(BluetoothShare.CONTENT_URI + "/" + info.mID);
Intent baseIntent = new Intent().setDataAndNormalize(contentUri)
.setClassName(Constants.THIS_PACKAGE_NAME,
BluetoothOppReceiver.class.getName());
Notification.Action actionDecline =
new Notification.Action.Builder(Icon.createWithResource(mContext,
R.drawable.ic_decline),
mContext.getText(R.string.incoming_file_confirm_cancel),
PendingIntent.getBroadcast(mContext, 0,
new Intent(baseIntent).setAction(Constants.ACTION_DECLINE),
0)).build();
Notification.Action actionAccept = new Notification.Action.Builder(
Icon.createWithResource(mContext,R.drawable.ic_accept),
mContext.getText(R.string.incoming_file_confirm_ok),
PendingIntent.getBroadcast(mContext, 0,
new Intent(baseIntent).setAction(Constants.ACTION_ACCEPT), 0)).build();
Notification public_n =
new Notification.Builder(mContext, OPP_NOTIFICATION_CHANNEL).setOnlyAlertOnce(
true)
.setOngoing(true)
.setWhen(info.mTimeStamp)
.addAction(actionDecline)
.addAction(actionAccept)
.setContentIntent(PendingIntent.getBroadcast(mContext, 0,
new Intent(baseIntent).setAction(
Constants.ACTION_INCOMING_FILE_CONFIRM), 0))
.setDeleteIntent(PendingIntent.getBroadcast(mContext, 0,
new Intent(baseIntent).setAction(Constants.ACTION_HIDE), 0))
.setColor(mContext.getResources()
.getColor(
com.android.internal.R.color
.system_notification_accent_color,
mContext.getTheme()))
.setContentTitle(mContext.getText(
R.string.incoming_file_confirm_Notification_title))
.setContentText(info.mFileName)
.setStyle(new Notification.BigTextStyle().bigText(mContext.getString(
R.string.incoming_file_confirm_Notification_content,
info.mDeviceName, info.mFileName)))
.setSubText(Formatter.formatFileSize(mContext, info.mTotalBytes))
.setSmallIcon(R.drawable.bt_incomming_file_notification)
.setLocalOnly(true)
.build();
Notification n =
new Notification.Builder(mContext, OPP_NOTIFICATION_CHANNEL).setOnlyAlertOnce(
true)
.setOngoing(true)
.setWhen(info.mTimeStamp)
.setContentIntent(PendingIntent.getBroadcast(mContext, 0,
new Intent(baseIntent).setAction(
Constants.ACTION_INCOMING_FILE_CONFIRM), 0))
.setDeleteIntent(PendingIntent.getBroadcast(mContext, 0,
new Intent(baseIntent).setAction(Constants.ACTION_HIDE), 0))
.setColor(mContext.getResources()
.getColor(
com.android.internal.R.color
.system_notification_accent_color,
mContext.getTheme()))
.setContentTitle(mContext.getText(
R.string.incoming_file_confirm_Notification_title))
.setContentText(info.mFileName)
.setStyle(new Notification.BigTextStyle().bigText(mContext.getString(
R.string.incoming_file_confirm_Notification_content,
info.mDeviceName, info.mFileName)))
.setSubText(Formatter.formatFileSize(mContext, info.mTotalBytes))
.setSmallIcon(R.drawable.bt_incomming_file_notification)
.setLocalOnly(true)
.setVisibility(Notification.VISIBILITY_PRIVATE)
.setPublicVersion(public_n)
.build();
mNotificationMgr.notify(NOTIFICATION_ID_PROGRESS, n);
}
cursor.close();
}
void cancelNotifications() {
if (V) {
Log.v(TAG, "cancelNotifications ");
}
mHandler.removeCallbacksAndMessages(null);
mNotificationMgr.cancelAll();
}
}