| /* |
| * 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, |
| PendingIntent.FLAG_IMMUTABLE)); |
| 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, |
| PendingIntent.FLAG_IMMUTABLE)) |
| .setDeleteIntent( |
| PendingIntent.getBroadcast(mContext, 0, deleteIntent, |
| PendingIntent.FLAG_IMMUTABLE)) |
| .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, |
| PendingIntent.FLAG_IMMUTABLE)) |
| .setDeleteIntent( |
| PendingIntent.getBroadcast(mContext, 0, deleteIntent, |
| PendingIntent.FLAG_IMMUTABLE)) |
| .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), |
| PendingIntent.FLAG_IMMUTABLE)).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), |
| PendingIntent.FLAG_IMMUTABLE)).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), |
| PendingIntent.FLAG_IMMUTABLE)) |
| .setDeleteIntent(PendingIntent.getBroadcast(mContext, 0, |
| new Intent(baseIntent).setAction(Constants.ACTION_HIDE), |
| PendingIntent.FLAG_IMMUTABLE)) |
| .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), |
| PendingIntent.FLAG_IMMUTABLE)) |
| .setDeleteIntent(PendingIntent.getBroadcast(mContext, 0, |
| new Intent(baseIntent).setAction(Constants.ACTION_HIDE), |
| PendingIntent.FLAG_IMMUTABLE)) |
| .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(); |
| } |
| } |