Merge "Allow cancelling of Bluetooth Beam transfers." into jb-dev
diff --git a/res/drawable-hdpi/ic_menu_cancel_holo_dark.png b/res/drawable-hdpi/ic_menu_cancel_holo_dark.png
new file mode 100644
index 0000000..2956109
--- /dev/null
+++ b/res/drawable-hdpi/ic_menu_cancel_holo_dark.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_menu_cancel_holo_dark.png b/res/drawable-mdpi/ic_menu_cancel_holo_dark.png
new file mode 100644
index 0000000..6ed1327
--- /dev/null
+++ b/res/drawable-mdpi/ic_menu_cancel_holo_dark.png
Binary files differ
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 06cc01c..005f770 100755
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -22,6 +22,8 @@
<string name="beam_progress">Incoming beam...</string>
<string name="beam_complete">Beam complete</string>
<string name="beam_failed">Beam failed</string>
+ <string name="beam_canceled">Beam canceled</string>
+ <string name="cancel">Cancel</string>
<string name="beam_touch_to_view">Touch to view</string>
<string name="connecting_headset">Connecting</string>
diff --git a/src/com/android/nfc/handover/BluetoothOppHandover.java b/src/com/android/nfc/handover/BluetoothOppHandover.java
index 1835aa8..3180e83 100644
--- a/src/com/android/nfc/handover/BluetoothOppHandover.java
+++ b/src/com/android/nfc/handover/BluetoothOppHandover.java
@@ -2,6 +2,7 @@
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
+import android.content.ActivityNotFoundException;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.Context;
@@ -116,11 +117,9 @@
void sendIntent() {
//TODO: either open up BluetoothOppLauncherActivity to all MIME types
// or gracefully handle mime types that can't be sent
- Log.d(TAG, "Sending handover intent for " + mDevice.getAddress());
Intent intent = new Intent();
intent.setPackage("com.android.bluetooth");
String mimeType = getMimeTypeForUri(mContext, mUris[0]);
- Log.d(TAG, "Determined mime type as " + mimeType);
intent.setType(mimeType);
intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mDevice);
if (mUris.length == 1) {
@@ -133,8 +132,11 @@
}
intent.putExtra(EXTRA_CONNECTION_HANDOVER, true);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- mContext.startActivity(intent);
-
+ try {
+ mContext.startActivity(intent);
+ } catch (ActivityNotFoundException e) {
+ Log.e(TAG, "Failed to handover file to bluetooth, mimeType not allowed.");
+ }
complete();
}
diff --git a/src/com/android/nfc/handover/HandoverManager.java b/src/com/android/nfc/handover/HandoverManager.java
index 79bcef1..9458472 100644
--- a/src/com/android/nfc/handover/HandoverManager.java
+++ b/src/com/android/nfc/handover/HandoverManager.java
@@ -117,6 +117,11 @@
static final String ACTION_WHITELIST_DEVICE =
"android.btopp.intent.action.WHITELIST_DEVICE";
+ static final String ACTION_CANCEL_HANDOVER_TRANSFER =
+ "com.android.nfc.handover.action.CANCEL_HANDOVER_TRANSFER";
+ static final String EXTRA_SOURCE_ADDRESS =
+ "com.android.nfc.handover.extra.SOURCE_ADDRESS";
+
static final int SOURCE_BLUETOOTH_INCOMING = 0;
static final int SOURCE_BLUETOOTH_OUTGOING = 1;
@@ -131,7 +136,9 @@
final NotificationManager mNotificationManager;
final HandoverPowerManager mHandoverPowerManager;
- // synchronized on HandoverManager.this
+ // Variables below synchronized on HandoverManager.this
+ final HashMap<Pair<String, Boolean>, HandoverTransfer> mTransfers;
+
BluetoothHeadset mBluetoothHeadset;
BluetoothA2dp mBluetoothA2dp;
BluetoothHeadsetHandover mBluetoothHeadsetHandover;
@@ -139,7 +146,6 @@
String mLocalBluetoothAddress;
int mNotificationId;
- HashMap<Pair<String, Boolean>, HandoverTransfer> mTransfers;
static class BluetoothHandoverData {
public boolean valid = false;
@@ -232,20 +238,24 @@
*/
class HandoverTransfer implements Handler.Callback,
MediaScannerConnection.OnScanCompletedListener {
+ // In the states below we still accept new file transfer
static final int STATE_NEW = 0;
static final int STATE_IN_PROGRESS = 1;
static final int STATE_W4_NEXT_TRANSFER = 2;
+
+ // In the states below no new files are accepted.
static final int STATE_W4_MEDIA_SCANNER = 3;
static final int STATE_FAILED = 4;
static final int STATE_SUCCESS = 5;
static final int STATE_CANCELLED = 6;
static final int MSG_NEXT_TRANSFER_TIMER = 0;
+ static final int MSG_TRANSFER_TIMEOUT = 1;
// We need to receive an update within this time period
// to still consider this transfer to be "alive" (ie
// a reason to keep the handover transport enabled).
- static final int ALIVE_CHECK_MS = 20000;
+ static final int ALIVE_CHECK_MS = 10000;
// The amount of time to wait for a new transfer
// once the current one completes.
@@ -258,6 +268,7 @@
final boolean incoming; // whether this is an incoming transfer
final int notificationId; // Unique ID of this transfer used for notifications
final Handler handler;
+ final PendingIntent cancelIntent;
int state;
Long lastUpdate; // Last time an event occurred for this transfer
@@ -285,8 +296,11 @@
this.sourceAddress = sourceAddress;
this.incoming = incoming;
this.handler = new Handler(mContext.getMainLooper(), this);
+ this.cancelIntent = buildCancelIntent();
this.urisScanned = 0;
this.device = mBluetoothAdapter.getRemoteDevice(sourceAddress);
+
+ handler.sendEmptyMessageDelayed(MSG_TRANSFER_TIMEOUT, ALIVE_CHECK_MS);
}
public synchronized void updateFileProgress(float progress) {
@@ -330,17 +344,23 @@
public synchronized boolean isRunning() {
if (state != STATE_NEW && state != STATE_IN_PROGRESS && state != STATE_W4_NEXT_TRANSFER) {
return false;
- }
-
- // Check that we've made progress
- Long currentTime = SystemClock.elapsedRealtime();
- if (currentTime - lastUpdate > ALIVE_CHECK_MS) {
- return false;
} else {
return true;
}
}
+ synchronized void cancel() {
+ if (!isRunning()) return;
+
+ // Delete all files received so far
+ for (Uri uri : btUris) {
+ File file = new File(uri.getPath());
+ if (file.exists()) file.delete();
+ }
+
+ updateStateAndNotification(STATE_CANCELLED);
+ }
+
synchronized void updateNotification() {
if (!incoming) return; // No notifications for outgoing transfers
@@ -352,6 +372,10 @@
notBuilder.setSmallIcon(android.R.drawable.stat_sys_download);
notBuilder.setTicker(mContext.getString(R.string.beam_progress));
notBuilder.setContentTitle(mContext.getString(R.string.beam_progress));
+ notBuilder.addAction(R.drawable.ic_menu_cancel_holo_dark,
+ mContext.getString(R.string.cancel), cancelIntent);
+ notBuilder.setDeleteIntent(cancelIntent);
+ notBuilder.setWhen(0);
// We do have progress indication on a per-file basis, but in a multi-file
// transfer we don't know the total progress. So for now, just show an
// indeterminate progress bar.
@@ -362,6 +386,7 @@
notBuilder.setTicker(mContext.getString(R.string.beam_complete));
notBuilder.setContentTitle(mContext.getString(R.string.beam_complete));
notBuilder.setContentText(mContext.getString(R.string.beam_touch_to_view));
+ notBuilder.setWhen(0);
Intent viewIntent = buildViewIntent();
PendingIntent contentIntent = PendingIntent.getActivity(mContext, 0, viewIntent, 0);
@@ -371,10 +396,17 @@
// Play Beam success sound
NfcService.getInstance().playSound(NfcService.SOUND_END);
} else if (state == STATE_FAILED) {
- notBuilder.setAutoCancel(true);
+ notBuilder.setAutoCancel(false);
notBuilder.setSmallIcon(android.R.drawable.stat_sys_download_done);
notBuilder.setTicker(mContext.getString(R.string.beam_failed));
notBuilder.setContentTitle(mContext.getString(R.string.beam_failed));
+ notBuilder.setWhen(0);
+ } else if (state == STATE_CANCELLED) {
+ notBuilder.setAutoCancel(false);
+ notBuilder.setSmallIcon(android.R.drawable.stat_sys_download_done);
+ notBuilder.setTicker(mContext.getString(R.string.beam_canceled));
+ notBuilder.setContentTitle(mContext.getString(R.string.beam_canceled));
+ notBuilder.setWhen(0);
} else {
return;
}
@@ -385,6 +417,12 @@
synchronized void updateStateAndNotification(int newState) {
this.state = newState;
this.lastUpdate = SystemClock.elapsedRealtime();
+
+ if (handler.hasMessages(MSG_TRANSFER_TIMEOUT)) {
+ // Update timeout timer
+ handler.removeMessages(MSG_TRANSFER_TIMEOUT);
+ handler.sendEmptyMessageDelayed(MSG_TRANSFER_TIMEOUT, ALIVE_CHECK_MS);
+ }
updateNotification();
}
@@ -447,8 +485,18 @@
public boolean handleMessage(Message msg) {
if (msg.what == MSG_NEXT_TRANSFER_TIMER) {
// We didn't receive a new transfer in time, finalize this one
- processFiles();
+ if (incoming) {
+ processFiles();
+ } else {
+ updateStateAndNotification(STATE_SUCCESS);
+ }
return true;
+ } else if (msg.what == MSG_TRANSFER_TIMEOUT) {
+ // No update on this transfer for a while, check
+ // to see if it's still running, and fail it if it is.
+ if (isRunning()) {
+ updateStateAndNotification(STATE_FAILED);
+ }
}
return false;
}
@@ -492,6 +540,14 @@
return viewIntent;
}
+ PendingIntent buildCancelIntent() {
+ Intent intent = new Intent(ACTION_CANCEL_HANDOVER_TRANSFER);
+ intent.putExtra(EXTRA_SOURCE_ADDRESS, sourceAddress);
+ PendingIntent pi = PendingIntent.getBroadcast(mContext, 0, intent, 0);
+
+ return pi;
+ }
+
synchronized File generateMultiplePath(String beamRoot) {
// Generate a unique directory with the date
String format = "yyyy-MM-dd";
@@ -508,24 +564,27 @@
return newFile;
}
-
-
}
- synchronized HandoverTransfer getHandoverTransfer(String sourceAddress, boolean incoming) {
+ synchronized HandoverTransfer getOrCreateHandoverTransfer(String sourceAddress, boolean incoming,
+ boolean create) {
Pair<String, Boolean> key = new Pair<String, Boolean>(sourceAddress, incoming);
if (mTransfers.containsKey(key)) {
HandoverTransfer transfer = mTransfers.get(key);
if (transfer.isRunning()) {
return transfer;
} else {
- // Remove old transfer; new one will be created below
- mTransfers.remove(key);
+ if (create) mTransfers.remove(key); // new one created below
}
}
- HandoverTransfer transfer = new HandoverTransfer(sourceAddress, incoming);
- mTransfers.put(key, transfer);
- return transfer;
+ if (create) {
+ HandoverTransfer transfer = new HandoverTransfer(sourceAddress, incoming);
+ mTransfers.put(key, transfer);
+
+ return transfer;
+ } else {
+ return null;
+ }
}
public HandoverManager(Context context) {
@@ -543,6 +602,7 @@
IntentFilter filter = new IntentFilter(ACTION_BT_OPP_TRANSFER_DONE);
filter.addAction(ACTION_BT_OPP_TRANSFER_PROGRESS);
filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
+ filter.addAction(ACTION_CANCEL_HANDOVER_TRANSFER);
mContext.registerReceiver(mReceiver, filter, HANDOVER_STATUS_PERMISSION, null);
}
@@ -666,8 +726,8 @@
}
// Create the initial transfer object
- HandoverTransfer transfer = getHandoverTransfer(bluetoothData.device.getAddress(),
- true);
+ HandoverTransfer transfer = getOrCreateHandoverTransfer(
+ bluetoothData.device.getAddress(), true, true);
transfer.updateNotification();
}
@@ -715,6 +775,8 @@
public void doHandoverUri(Uri[] uris, NdefMessage m) {
BluetoothHandoverData data = parse(m);
if (data != null && data.valid) {
+ // Register a new handover transfer object
+ getOrCreateHandoverTransfer(data.device.getAddress(), false, true);
BluetoothOppHandover handover = new BluetoothOppHandover(mContext, data.device,
uris, mHandoverPowerManager, data.carrierActivating);
handover.start();
@@ -928,9 +990,16 @@
}
return;
+ } else if (action.equals(ACTION_CANCEL_HANDOVER_TRANSFER)) {
+ String sourceAddress = intent.getStringExtra(EXTRA_SOURCE_ADDRESS);
+ HandoverTransfer transfer = getOrCreateHandoverTransfer(sourceAddress, true,
+ false);
+ if (transfer != null) {
+ transfer.cancel();
+ }
} else if (action.equals(ACTION_BT_OPP_TRANSFER_PROGRESS) ||
action.equals(ACTION_BT_OPP_TRANSFER_DONE)) {
- // Clean up old transfers in progress
+ // Clean up old transfers no longer in progress
cleanupTransfers();
int direction = intent.getIntExtra(EXTRA_BT_OPP_TRANSFER_DIRECTION, -1);
@@ -939,8 +1008,18 @@
if (direction == -1 || id == -1 || sourceAddress == null) return;
boolean incoming = (direction == DIRECTION_BLUETOOTH_INCOMING);
- HandoverTransfer transfer = getHandoverTransfer(sourceAddress, incoming);
- if (transfer == null) return;
+
+ HandoverTransfer transfer = getOrCreateHandoverTransfer(sourceAddress, incoming,
+ false);
+ if (transfer == null) {
+ // There is no transfer running for this source address; most likely
+ // the transfer was cancelled. We need to tell BT OPP to stop transferring
+ // in case this was an incoming transfer
+ Intent cancelIntent = new Intent("android.btopp.intent.action.STOP_HANDOVER_TRANSFER");
+ cancelIntent.putExtra(EXTRA_BT_OPP_TRANSFER_ID, id);
+ mContext.sendBroadcast(cancelIntent);
+ return;
+ }
if (action.equals(ACTION_BT_OPP_TRANSFER_DONE)) {
int handoverStatus = intent.getIntExtra(EXTRA_BT_OPP_TRANSFER_STATUS,
@@ -963,8 +1042,6 @@
}
}
}
-
};
-
}