Add Beam progress indicator.
Now that the Bluetooth stack supports pushing
the count over OPP, we can more reliably indicate
transfer progress.
Also added a notification on the outgoing side
to show progress.
Change-Id: Id0c5e1750a732910d6a5895ab3d318fdfe752a30
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 50cbf63..a4bda3b 100755
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -20,6 +20,7 @@
<string name="touch">Touch to beam</string>
<string name="beam_progress">Incoming beam...</string>
+ <string name="beam_outgoing">Beaming...</string>
<string name="beam_complete">Beam complete</string>
<string name="beam_failed">Beam did not complete</string>
<string name="beam_canceled">Beam canceled</string>
diff --git a/src/com/android/nfc/handover/HandoverService.java b/src/com/android/nfc/handover/HandoverService.java
index 45521da..181509d 100644
--- a/src/com/android/nfc/handover/HandoverService.java
+++ b/src/com/android/nfc/handover/HandoverService.java
@@ -47,9 +47,16 @@
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 String EXTRA_INCOMING =
+ "com.android.nfc.handover.extra.INCOMING";
+
+ static final String ACTION_HANDOVER_STARTED =
+ "android.btopp.intent.action.BT_OPP_HANDOVER_STARTED";
+
static final String ACTION_BT_OPP_TRANSFER_PROGRESS =
"android.btopp.intent.action.BT_OPP_TRANSFER_PROGRESS";
@@ -85,6 +92,9 @@
static final String EXTRA_BT_OPP_TRANSFER_URI =
"android.btopp.intent.extra.BT_OPP_TRANSFER_URI";
+ public static final String EXTRA_BT_OPP_OBJECT_COUNT =
+ "android.btopp.intent.extra.BT_OPP_OBJECT_COUNT";
+
// permission needed to be able to receive handover status requests
static final String HANDOVER_STATUS_PERMISSION =
"com.android.permission.HANDOVER_STATUS";
@@ -125,6 +135,7 @@
filter.addAction(ACTION_BT_OPP_TRANSFER_PROGRESS);
filter.addAction(ACTION_CANCEL_HANDOVER_TRANSFER);
filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
+ filter.addAction(ACTION_HANDOVER_STARTED);
registerReceiver(mReceiver, filter, HANDOVER_STATUS_PERMISSION, mHandler);
}
@@ -286,7 +297,8 @@
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (action.equals(BluetoothAdapter.ACTION_STATE_CHANGED)) {
- int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR);
+ int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE,
+ BluetoothAdapter.ERROR);
if (state == BluetoothAdapter.STATE_ON) {
// If there is a pending headset pairing, start it
if (mBluetoothHeadsetHandover != null &&
@@ -294,39 +306,46 @@
mBluetoothHeadsetHandover.start();
}
- // Start any pending transfers
+ // Start any pending file transfers
startPendingTransfers();
} else if (state == BluetoothAdapter.STATE_OFF) {
mBluetoothEnabledByNfc = false;
mBluetoothHeadsetConnected = false;
}
- }
- else if (action.equals(ACTION_CANCEL_HANDOVER_TRANSFER)) {
+ } else if (action.equals(ACTION_CANCEL_HANDOVER_TRANSFER)) {
String sourceAddress = intent.getStringExtra(EXTRA_SOURCE_ADDRESS);
- HandoverTransfer transfer = findHandoverTransfer(sourceAddress, true);
+ boolean incoming = (intent.getIntExtra(EXTRA_INCOMING, 1)) == 1;
+ HandoverTransfer transfer = findHandoverTransfer(sourceAddress, incoming);
if (transfer != null) {
if (DBG) Log.d(TAG, "Cancelling transfer " +
Integer.toString(transfer.mTransferId));
transfer.cancel();
}
} else if (action.equals(ACTION_BT_OPP_TRANSFER_PROGRESS) ||
- action.equals(ACTION_BT_OPP_TRANSFER_DONE)) {
+ action.equals(ACTION_BT_OPP_TRANSFER_DONE) ||
+ action.equals(ACTION_HANDOVER_STARTED)) {
int direction = intent.getIntExtra(EXTRA_BT_OPP_TRANSFER_DIRECTION, -1);
int id = intent.getIntExtra(EXTRA_BT_OPP_TRANSFER_ID, -1);
+ if (action.equals(ACTION_HANDOVER_STARTED)) {
+ // This is always for incoming transfers
+ direction = DIRECTION_BLUETOOTH_INCOMING;
+ }
String sourceAddress = intent.getStringExtra(EXTRA_BT_OPP_ADDRESS);
- if (direction == -1 || id == -1 || sourceAddress == null) return;
+ if (direction == -1 || sourceAddress == null) return;
boolean incoming = (direction == DIRECTION_BLUETOOTH_INCOMING);
HandoverTransfer transfer = findHandoverTransfer(sourceAddress, incoming);
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
- if (DBG) Log.d(TAG, "Didn't find transfer, stopping");
- Intent cancelIntent = new Intent("android.btopp.intent.action.STOP_HANDOVER_TRANSFER");
- cancelIntent.putExtra(EXTRA_BT_OPP_TRANSFER_ID, id);
- sendBroadcast(cancelIntent);
+ // the transfer was cancelled. We need to tell BT OPP to stop transferring.
+ if (id != -1) {
+ if (DBG) Log.d(TAG, "Didn't find transfer, stopping");
+ Intent cancelIntent = new Intent(
+ "android.btopp.intent.action.STOP_HANDOVER_TRANSFER");
+ cancelIntent.putExtra(EXTRA_BT_OPP_TRANSFER_ID, id);
+ sendBroadcast(cancelIntent);
+ }
return;
}
if (action.equals(ACTION_BT_OPP_TRANSFER_DONE)) {
@@ -346,6 +365,11 @@
} else if (action.equals(ACTION_BT_OPP_TRANSFER_PROGRESS)) {
float progress = intent.getFloatExtra(EXTRA_BT_OPP_TRANSFER_PROGRESS, 0.0f);
transfer.updateFileProgress(progress);
+ } else if (action.equals(ACTION_HANDOVER_STARTED)) {
+ int count = intent.getIntExtra(EXTRA_BT_OPP_OBJECT_COUNT, 0);
+ if (count > 0) {
+ transfer.setObjectCount(count);
+ }
}
}
}
diff --git a/src/com/android/nfc/handover/HandoverTransfer.java b/src/com/android/nfc/handover/HandoverTransfer.java
index 019f0ac..8369678 100644
--- a/src/com/android/nfc/handover/HandoverTransfer.java
+++ b/src/com/android/nfc/handover/HandoverTransfer.java
@@ -89,6 +89,9 @@
// Variables below are only accessed on the main thread
int mState;
+ int mCurrentCount;
+ int mSuccessCount;
+ int mTotalCount;
boolean mCalledBack;
Long mLastUpdate; // Last time an event occurred for this transfer
float mProgress; // Progress in range [0..1]
@@ -107,6 +110,8 @@
mRemoteDevice = pendingTransfer.remoteDevice;
mIncoming = pendingTransfer.incoming;
mTransferId = pendingTransfer.id;
+ // For incoming transfers, count can be set later
+ mTotalCount = (pendingTransfer.uris != null) ? pendingTransfer.uris.length : 0;
mLastUpdate = SystemClock.elapsedRealtime();
mProgress = 0.0f;
mState = STATE_NEW;
@@ -115,8 +120,10 @@
mPaths = new ArrayList<String>();
mMimeTypes = new HashMap<String, String>();
mMediaUris = new HashMap<String, Uri>();
- mCancelIntent = buildCancelIntent();
+ mCancelIntent = buildCancelIntent(mIncoming);
mUrisScanned = 0;
+ mCurrentCount = 0;
+ mSuccessCount = 0;
mHandler = new Handler(Looper.getMainLooper(), this);
mHandler.sendEmptyMessageDelayed(MSG_TRANSFER_TIMEOUT, ALIVE_CHECK_MS);
@@ -148,9 +155,11 @@
public void finishTransfer(boolean success, Uri uri, String mimeType) {
if (!isRunning()) return; // Ignore when we're no longer running
+ mCurrentCount++;
if (success && uri != null) {
+ mSuccessCount++;
if (DBG) Log.d(TAG, "Transfer success, uri " + uri + " mimeType " + mimeType);
- this.mProgress = 1.0f;
+ mProgress = 0.0f;
if (mimeType == null) {
mimeType = BluetoothOppHandover.getMimeTypeForUri(mContext, uri);
}
@@ -165,8 +174,16 @@
// Do wait to see if there's another file coming.
}
mHandler.removeMessages(MSG_NEXT_TRANSFER_TIMER);
- mHandler.sendEmptyMessageDelayed(MSG_NEXT_TRANSFER_TIMER, WAIT_FOR_NEXT_TRANSFER_MS);
- updateStateAndNotification(STATE_W4_NEXT_TRANSFER);
+ if (mCurrentCount == mTotalCount) {
+ if (mIncoming) {
+ processFiles();
+ } else {
+ updateStateAndNotification(mSuccessCount > 0 ? STATE_SUCCESS : STATE_FAILED);
+ }
+ } else {
+ mHandler.sendEmptyMessageDelayed(MSG_NEXT_TRANSFER_TIMER, WAIT_FOR_NEXT_TRANSFER_MS);
+ updateStateAndNotification(STATE_W4_NEXT_TRANSFER);
+ }
}
public boolean isRunning() {
@@ -177,6 +194,10 @@
}
}
+ public void setObjectCount(int objectCount) {
+ mTotalCount = objectCount;
+ }
+
void cancel() {
if (!isRunning()) return;
@@ -190,35 +211,46 @@
}
void updateNotification() {
- if (!mIncoming) return; // No notifications for outgoing transfers
-
Builder notBuilder = new Notification.Builder(mContext);
+ String beamString;
+ if (mIncoming) {
+ beamString = mContext.getString(R.string.beam_progress);
+ } else {
+ beamString = mContext.getString(R.string.beam_outgoing);
+ }
if (mState == STATE_NEW || mState == STATE_IN_PROGRESS ||
mState == STATE_W4_NEXT_TRANSFER || mState == STATE_W4_MEDIA_SCANNER) {
notBuilder.setAutoCancel(false);
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.setTicker(beamString);
+ notBuilder.setContentTitle(beamString);
notBuilder.addAction(R.drawable.ic_menu_cancel_holo_dark,
mContext.getString(R.string.cancel), mCancelIntent);
- notBuilder.setDeleteIntent(mCancelIntent);
- // 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.
- notBuilder.setProgress(100, 0, true);
+ float progress = 0;
+ if (mTotalCount > 0) {
+ float progressUnit = 1.0f / mTotalCount;
+ progress = (float) mCurrentCount * progressUnit + mProgress * progressUnit;
+ }
+ if (mTotalCount > 0 && progress > 0) {
+ notBuilder.setProgress(100, (int) (100 * progress), false);
+ } else {
+ notBuilder.setProgress(100, 0, true);
+ }
} else if (mState == STATE_SUCCESS) {
notBuilder.setAutoCancel(true);
notBuilder.setSmallIcon(android.R.drawable.stat_sys_download_done);
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));
- Intent viewIntent = buildViewIntent();
- PendingIntent contentIntent = PendingIntent.getActivity(
- mContext, 0, viewIntent, 0, null);
+ if (mIncoming) {
+ notBuilder.setContentText(mContext.getString(R.string.beam_touch_to_view));
+ Intent viewIntent = buildViewIntent();
+ PendingIntent contentIntent = PendingIntent.getActivity(
+ mContext, mTransferId, viewIntent, 0, null);
- notBuilder.setContentIntent(contentIntent);
+ notBuilder.setContentIntent(contentIntent);
+ }
} else if (mState == STATE_FAILED) {
notBuilder.setAutoCancel(false);
notBuilder.setSmallIcon(android.R.drawable.stat_sys_download_done);
@@ -324,7 +356,7 @@
if (mIncoming) {
processFiles();
} else {
- updateStateAndNotification(STATE_SUCCESS);
+ updateStateAndNotification(mSuccessCount > 0 ? STATE_SUCCESS : STATE_FAILED);
}
return true;
} else if (msg.what == MSG_TRANSFER_TIMEOUT) {
@@ -377,10 +409,12 @@
return viewIntent;
}
- PendingIntent buildCancelIntent() {
+ PendingIntent buildCancelIntent(boolean incoming) {
Intent intent = new Intent(HandoverService.ACTION_CANCEL_HANDOVER_TRANSFER);
intent.putExtra(HandoverService.EXTRA_SOURCE_ADDRESS, mRemoteDevice.getAddress());
- PendingIntent pi = PendingIntent.getBroadcast(mContext, 0, intent, 0);
+ intent.putExtra(HandoverService.EXTRA_INCOMING, incoming ? 1 : 0);
+ PendingIntent pi = PendingIntent.getBroadcast(mContext, mTransferId, intent,
+ PendingIntent.FLAG_ONE_SHOT);
return pi;
}