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 33 package com.android.bluetooth.opp; 34 35 import android.app.NotificationManager; 36 import android.bluetooth.BluetoothAdapter; 37 import android.bluetooth.BluetoothDevice; 38 import android.content.DialogInterface; 39 import android.content.Intent; 40 import android.database.ContentObserver; 41 import android.net.Uri; 42 import android.os.Bundle; 43 import android.os.Handler; 44 import android.text.format.Formatter; 45 import android.util.Log; 46 import android.view.View; 47 import android.widget.ProgressBar; 48 import android.widget.TextView; 49 import android.widget.Toast; 50 51 import com.android.bluetooth.R; 52 import com.android.internal.app.AlertActivity; 53 import com.android.internal.app.AlertController; 54 55 /** 56 * Handle all transfer related dialogs: -Ongoing transfer -Receiving one file 57 * dialog -Sending one file dialog -sending multiple files dialog -Complete 58 * transfer -receive -receive success, will trigger corresponding handler 59 * -receive fail dialog -send -send success dialog -send fail dialog -Other 60 * dialogs - - DIALOG_RECEIVE_ONGOING will transition to 61 * DIALOG_RECEIVE_COMPLETE_SUCCESS or DIALOG_RECEIVE_COMPLETE_FAIL 62 * DIALOG_SEND_ONGOING will transition to DIALOG_SEND_COMPLETE_SUCCESS or 63 * DIALOG_SEND_COMPLETE_FAIL 64 */ 65 public class BluetoothOppTransferActivity extends AlertActivity 66 implements DialogInterface.OnClickListener { 67 private static final String TAG = "BluetoothOppTransferActivity"; 68 private static final boolean D = Constants.DEBUG; 69 private static final boolean V = Constants.VERBOSE; 70 71 private Uri mUri; 72 73 // ongoing transfer-0 complete transfer-1 74 boolean mIsComplete; 75 76 private BluetoothOppTransferInfo mTransInfo; 77 78 private ProgressBar mProgressTransfer; 79 80 private TextView mPercentView; 81 82 private AlertController.AlertParams mPara; 83 84 private View mView = null; 85 86 private TextView mLine1View, mLine2View, mLine3View, mLine5View; 87 88 private int mWhichDialog; 89 90 private BluetoothAdapter mAdapter; 91 92 // Dialogs definition: 93 // Receive progress dialog 94 public static final int DIALOG_RECEIVE_ONGOING = 0; 95 96 // Receive complete and success dialog 97 public static final int DIALOG_RECEIVE_COMPLETE_SUCCESS = 1; 98 99 // Receive complete and fail dialog: will display some fail reason 100 public static final int DIALOG_RECEIVE_COMPLETE_FAIL = 2; 101 102 // Send progress dialog 103 public static final int DIALOG_SEND_ONGOING = 3; 104 105 // Send complete and success dialog 106 public static final int DIALOG_SEND_COMPLETE_SUCCESS = 4; 107 108 // Send complete and fail dialog: will let user retry 109 public static final int DIALOG_SEND_COMPLETE_FAIL = 5; 110 111 /** Observer to get notified when the content observer's data changes */ 112 private BluetoothTransferContentObserver mObserver; 113 114 // do not update button during activity creating, only update when db 115 // changes after activity created 116 private boolean mNeedUpdateButton = false; 117 118 private class BluetoothTransferContentObserver extends ContentObserver { 119 BluetoothTransferContentObserver() { 120 super(new Handler()); 121 } 122 123 @Override 124 public void onChange(boolean selfChange) { 125 if (V) { 126 Log.v(TAG, "received db changes."); 127 } 128 mNeedUpdateButton = true; 129 updateProgressbar(); 130 } 131 } 132 133 @Override 134 protected void onCreate(Bundle savedInstanceState) { 135 super.onCreate(savedInstanceState); 136 Intent intent = getIntent(); 137 mUri = intent.getData(); 138 139 mTransInfo = new BluetoothOppTransferInfo(); 140 mTransInfo = BluetoothOppUtility.queryRecord(this, mUri); 141 if (mTransInfo == null) { 142 if (V) { 143 Log.e(TAG, "Error: Can not get data from db"); 144 } 145 finish(); 146 return; 147 } 148 149 mIsComplete = BluetoothShare.isStatusCompleted(mTransInfo.mStatus); 150 151 displayWhichDialog(); 152 153 // update progress bar for ongoing transfer 154 if (!mIsComplete) { 155 mObserver = new BluetoothTransferContentObserver(); 156 getContentResolver().registerContentObserver(BluetoothShare.CONTENT_URI, true, 157 mObserver); 158 } 159 160 if (mWhichDialog != DIALOG_SEND_ONGOING && mWhichDialog != DIALOG_RECEIVE_ONGOING) { 161 // set this record to INVISIBLE 162 BluetoothOppUtility.updateVisibilityToHidden(this, mUri); 163 } 164 165 mAdapter = BluetoothAdapter.getDefaultAdapter(); 166 167 // Set up the "dialog" 168 setUpDialog(); 169 } 170 171 @Override 172 protected void onDestroy() { 173 if (D) { 174 Log.d(TAG, "onDestroy()"); 175 } 176 177 if (mObserver != null) { 178 getContentResolver().unregisterContentObserver(mObserver); 179 } 180 super.onDestroy(); 181 } 182 183 private void displayWhichDialog() { 184 int direction = mTransInfo.mDirection; 185 boolean isSuccess = BluetoothShare.isStatusSuccess(mTransInfo.mStatus); 186 boolean isComplete = BluetoothShare.isStatusCompleted(mTransInfo.mStatus); 187 188 if (direction == BluetoothShare.DIRECTION_INBOUND) { 189 if (isComplete) { 190 if (isSuccess) { 191 // should not go here 192 mWhichDialog = DIALOG_RECEIVE_COMPLETE_SUCCESS; 193 } else if (!isSuccess) { 194 mWhichDialog = DIALOG_RECEIVE_COMPLETE_FAIL; 195 } 196 } else if (!isComplete) { 197 mWhichDialog = DIALOG_RECEIVE_ONGOING; 198 } 199 } else if (direction == BluetoothShare.DIRECTION_OUTBOUND) { 200 if (isComplete) { 201 if (isSuccess) { 202 mWhichDialog = DIALOG_SEND_COMPLETE_SUCCESS; 203 204 } else if (!isSuccess) { 205 mWhichDialog = DIALOG_SEND_COMPLETE_FAIL; 206 } 207 } else if (!isComplete) { 208 mWhichDialog = DIALOG_SEND_ONGOING; 209 } 210 } 211 212 if (V) { 213 Log.v(TAG, " WhichDialog/dir/isComplete/failOrSuccess" + mWhichDialog + direction 214 + isComplete + isSuccess); 215 } 216 } 217 218 private void setUpDialog() { 219 // final AlertController.AlertParams p = mAlertParams; 220 mPara = mAlertParams; 221 mPara.mTitle = getString(R.string.download_title); 222 223 if ((mWhichDialog == DIALOG_RECEIVE_ONGOING) || (mWhichDialog == DIALOG_SEND_ONGOING)) { 224 mPara.mPositiveButtonText = getString(R.string.download_ok); 225 mPara.mPositiveButtonListener = this; 226 mPara.mNegativeButtonText = getString(R.string.download_cancel); 227 mPara.mNegativeButtonListener = this; 228 } else if (mWhichDialog == DIALOG_RECEIVE_COMPLETE_SUCCESS) { 229 mPara.mPositiveButtonText = getString(R.string.download_succ_ok); 230 mPara.mPositiveButtonListener = this; 231 } else if (mWhichDialog == DIALOG_RECEIVE_COMPLETE_FAIL) { 232 mPara.mIconAttrId = android.R.attr.alertDialogIcon; 233 mPara.mPositiveButtonText = getString(R.string.download_fail_ok); 234 mPara.mPositiveButtonListener = this; 235 } else if (mWhichDialog == DIALOG_SEND_COMPLETE_SUCCESS) { 236 mPara.mPositiveButtonText = getString(R.string.upload_succ_ok); 237 mPara.mPositiveButtonListener = this; 238 } else if (mWhichDialog == DIALOG_SEND_COMPLETE_FAIL) { 239 mPara.mIconAttrId = android.R.attr.alertDialogIcon; 240 mPara.mPositiveButtonText = getString(R.string.upload_fail_ok); 241 mPara.mPositiveButtonListener = this; 242 mPara.mNegativeButtonText = getString(R.string.upload_fail_cancel); 243 mPara.mNegativeButtonListener = this; 244 } 245 mPara.mView = createView(); 246 setupAlert(); 247 } 248 249 private View createView() { 250 251 mView = getLayoutInflater().inflate(R.layout.file_transfer, null); 252 253 mProgressTransfer = (ProgressBar) mView.findViewById(R.id.progress_transfer); 254 mPercentView = (TextView) mView.findViewById(R.id.progress_percent); 255 256 customizeViewContent(); 257 258 // no need update button when activity creating 259 mNeedUpdateButton = false; 260 updateProgressbar(); 261 262 return mView; 263 } 264 265 /** 266 * customize the content of view 267 */ 268 private void customizeViewContent() { 269 String tmp; 270 271 if (mWhichDialog == DIALOG_RECEIVE_ONGOING 272 || mWhichDialog == DIALOG_RECEIVE_COMPLETE_SUCCESS) { 273 mLine1View = (TextView) mView.findViewById(R.id.line1_view); 274 tmp = getString(R.string.download_line1, mTransInfo.mDeviceName); 275 mLine1View.setText(tmp); 276 mLine2View = (TextView) mView.findViewById(R.id.line2_view); 277 tmp = getString(R.string.download_line2, mTransInfo.mFileName); 278 mLine2View.setText(tmp); 279 mLine3View = (TextView) mView.findViewById(R.id.line3_view); 280 tmp = getString(R.string.download_line3, 281 Formatter.formatFileSize(this, mTransInfo.mTotalBytes)); 282 mLine3View.setText(tmp); 283 mLine5View = (TextView) mView.findViewById(R.id.line5_view); 284 if (mWhichDialog == DIALOG_RECEIVE_ONGOING) { 285 tmp = getString(R.string.download_line5); 286 } else if (mWhichDialog == DIALOG_RECEIVE_COMPLETE_SUCCESS) { 287 tmp = getString(R.string.download_succ_line5); 288 } 289 mLine5View.setText(tmp); 290 } else if (mWhichDialog == DIALOG_SEND_ONGOING 291 || mWhichDialog == DIALOG_SEND_COMPLETE_SUCCESS) { 292 mLine1View = (TextView) mView.findViewById(R.id.line1_view); 293 tmp = getString(R.string.upload_line1, mTransInfo.mDeviceName); 294 mLine1View.setText(tmp); 295 mLine2View = (TextView) mView.findViewById(R.id.line2_view); 296 tmp = getString(R.string.download_line2, mTransInfo.mFileName); 297 mLine2View.setText(tmp); 298 mLine3View = (TextView) mView.findViewById(R.id.line3_view); 299 tmp = getString(R.string.upload_line3, mTransInfo.mFileType, 300 Formatter.formatFileSize(this, mTransInfo.mTotalBytes)); 301 mLine3View.setText(tmp); 302 mLine5View = (TextView) mView.findViewById(R.id.line5_view); 303 if (mWhichDialog == DIALOG_SEND_ONGOING) { 304 tmp = getString(R.string.upload_line5); 305 } else if (mWhichDialog == DIALOG_SEND_COMPLETE_SUCCESS) { 306 tmp = getString(R.string.upload_succ_line5); 307 } 308 mLine5View.setText(tmp); 309 } else if (mWhichDialog == DIALOG_RECEIVE_COMPLETE_FAIL) { 310 if (mTransInfo.mStatus == BluetoothShare.STATUS_ERROR_SDCARD_FULL) { 311 mLine1View = (TextView) mView.findViewById(R.id.line1_view); 312 tmp = getString(R.string.bt_sm_2_1, mTransInfo.mDeviceName); 313 mLine1View.setText(tmp); 314 mLine2View = (TextView) mView.findViewById(R.id.line2_view); 315 tmp = getString(R.string.download_fail_line2, mTransInfo.mFileName); 316 mLine2View.setText(tmp); 317 mLine3View = (TextView) mView.findViewById(R.id.line3_view); 318 tmp = getString(R.string.bt_sm_2_2, 319 Formatter.formatFileSize(this, mTransInfo.mTotalBytes)); 320 mLine3View.setText(tmp); 321 } else { 322 mLine1View = (TextView) mView.findViewById(R.id.line1_view); 323 tmp = getString(R.string.download_fail_line1); 324 mLine1View.setText(tmp); 325 mLine2View = (TextView) mView.findViewById(R.id.line2_view); 326 tmp = getString(R.string.download_fail_line2, mTransInfo.mFileName); 327 mLine2View.setText(tmp); 328 mLine3View = (TextView) mView.findViewById(R.id.line3_view); 329 tmp = getString(R.string.download_fail_line3, 330 BluetoothOppUtility.getStatusDescription(this, mTransInfo.mStatus, 331 mTransInfo.mDeviceName)); 332 mLine3View.setText(tmp); 333 } 334 mLine5View = (TextView) mView.findViewById(R.id.line5_view); 335 mLine5View.setVisibility(View.GONE); 336 } else if (mWhichDialog == DIALOG_SEND_COMPLETE_FAIL) { 337 mLine1View = (TextView) mView.findViewById(R.id.line1_view); 338 tmp = getString(R.string.upload_fail_line1, mTransInfo.mDeviceName); 339 mLine1View.setText(tmp); 340 mLine2View = (TextView) mView.findViewById(R.id.line2_view); 341 tmp = getString(R.string.upload_fail_line1_2, mTransInfo.mFileName); 342 mLine2View.setText(tmp); 343 mLine3View = (TextView) mView.findViewById(R.id.line3_view); 344 tmp = getString(R.string.download_fail_line3, 345 BluetoothOppUtility.getStatusDescription(this, mTransInfo.mStatus, 346 mTransInfo.mDeviceName)); 347 mLine3View.setText(tmp); 348 mLine5View = (TextView) mView.findViewById(R.id.line5_view); 349 mLine5View.setVisibility(View.GONE); 350 } 351 352 if (BluetoothShare.isStatusError(mTransInfo.mStatus)) { 353 mProgressTransfer.setVisibility(View.GONE); 354 mPercentView.setVisibility(View.GONE); 355 } 356 } 357 358 @Override 359 public void onClick(DialogInterface dialog, int which) { 360 switch (which) { 361 case DialogInterface.BUTTON_POSITIVE: 362 if (mWhichDialog == DIALOG_RECEIVE_COMPLETE_SUCCESS) { 363 // "Open" - open receive file 364 BluetoothOppUtility.openReceivedFile(this, mTransInfo.mFileName, 365 mTransInfo.mFileType, mTransInfo.mTimeStamp, mUri); 366 367 // make current transfer "hidden" 368 BluetoothOppUtility.updateVisibilityToHidden(this, mUri); 369 370 // clear correspondent notification item 371 ((NotificationManager) getSystemService(NOTIFICATION_SERVICE)).cancel( 372 mTransInfo.mID); 373 } else if (mWhichDialog == DIALOG_SEND_COMPLETE_FAIL) { 374 // "try again" 375 376 // make current transfer "hidden" 377 BluetoothOppUtility.updateVisibilityToHidden(this, mUri); 378 379 // clear correspondent notification item 380 ((NotificationManager) getSystemService(NOTIFICATION_SERVICE)).cancel( 381 mTransInfo.mID); 382 383 // retry the failed transfer 384 Uri uri = BluetoothOppUtility.originalUri(Uri.parse(mTransInfo.mFileUri)); 385 BluetoothOppSendFileInfo sendFileInfo = 386 BluetoothOppSendFileInfo.generateFileInfo(BluetoothOppTransferActivity 387 .this, uri, mTransInfo.mFileType, false); 388 uri = BluetoothOppUtility.generateUri(uri, sendFileInfo); 389 BluetoothOppUtility.putSendFileInfo(uri, sendFileInfo); 390 mTransInfo.mFileUri = uri.toString(); 391 BluetoothOppUtility.retryTransfer(this, mTransInfo); 392 393 BluetoothDevice remoteDevice = mAdapter.getRemoteDevice(mTransInfo.mDestAddr); 394 395 // Display toast message 396 Toast.makeText(this, this.getString(R.string.bt_toast_4, 397 BluetoothOppManager.getInstance(this).getDeviceName(remoteDevice)), 398 Toast.LENGTH_SHORT).show(); 399 400 } else if (mWhichDialog == DIALOG_SEND_COMPLETE_SUCCESS) { 401 BluetoothOppUtility.updateVisibilityToHidden(this, mUri); 402 ((NotificationManager) getSystemService(NOTIFICATION_SERVICE)).cancel( 403 mTransInfo.mID); 404 } 405 break; 406 407 case DialogInterface.BUTTON_NEGATIVE: 408 if (mWhichDialog == DIALOG_RECEIVE_ONGOING || mWhichDialog == DIALOG_SEND_ONGOING) { 409 // "Stop" button 410 this.getContentResolver().delete(mUri, null, null); 411 412 String msg = ""; 413 if (mWhichDialog == DIALOG_RECEIVE_ONGOING) { 414 msg = getString(R.string.bt_toast_3, mTransInfo.mDeviceName); 415 } else if (mWhichDialog == DIALOG_SEND_ONGOING) { 416 msg = getString(R.string.bt_toast_6, mTransInfo.mDeviceName); 417 } 418 Toast.makeText(this, msg, Toast.LENGTH_SHORT).show(); 419 420 ((NotificationManager) getSystemService(NOTIFICATION_SERVICE)).cancel( 421 mTransInfo.mID); 422 } else if (mWhichDialog == DIALOG_SEND_COMPLETE_FAIL) { 423 424 BluetoothOppUtility.updateVisibilityToHidden(this, mUri); 425 } 426 break; 427 } 428 finish(); 429 } 430 431 /** 432 * Update progress bar per data got from content provider 433 */ 434 private void updateProgressbar() { 435 mTransInfo = BluetoothOppUtility.queryRecord(this, mUri); 436 if (mTransInfo == null) { 437 if (V) { 438 Log.e(TAG, "Error: Can not get data from db"); 439 } 440 return; 441 } 442 443 // Set Transfer Max as 100. Percentage calculation would be done in setProgress API 444 mProgressTransfer.setMax(100); 445 446 if (mTransInfo.mTotalBytes != 0) { 447 if (V) { 448 Log.v(TAG, "mCurrentBytes: " + mTransInfo.mCurrentBytes + " mTotalBytes: " 449 + mTransInfo.mTotalBytes + " (" + (int) ((mTransInfo.mCurrentBytes * 100) 450 / mTransInfo.mTotalBytes) + "%)"); 451 } 452 mProgressTransfer.setProgress( 453 (int) ((mTransInfo.mCurrentBytes * 100) / mTransInfo.mTotalBytes)); 454 } else { 455 mProgressTransfer.setProgress(100); 456 } 457 458 mPercentView.setText(BluetoothOppUtility.formatProgressText(mTransInfo.mTotalBytes, 459 mTransInfo.mCurrentBytes)); 460 461 // Handle the case when DIALOG_RECEIVE_ONGOING evolve to 462 // DIALOG_RECEIVE_COMPLETE_SUCCESS/DIALOG_RECEIVE_COMPLETE_FAIL 463 // Handle the case when DIALOG_SEND_ONGOING evolve to 464 // DIALOG_SEND_COMPLETE_SUCCESS/DIALOG_SEND_COMPLETE_FAIL 465 if (!mIsComplete && BluetoothShare.isStatusCompleted(mTransInfo.mStatus) 466 && mNeedUpdateButton) { 467 if (mObserver != null) { 468 getContentResolver().unregisterContentObserver(mObserver); 469 mObserver = null; 470 } 471 displayWhichDialog(); 472 updateButton(); 473 customizeViewContent(); 474 } 475 } 476 477 /** 478 * Update button when one transfer goto complete from ongoing 479 */ 480 private void updateButton() { 481 if (mWhichDialog == DIALOG_RECEIVE_COMPLETE_SUCCESS) { 482 mAlert.getButton(DialogInterface.BUTTON_NEGATIVE).setVisibility(View.GONE); 483 mAlert.getButton(DialogInterface.BUTTON_POSITIVE) 484 .setText(getString(R.string.download_succ_ok)); 485 } else if (mWhichDialog == DIALOG_RECEIVE_COMPLETE_FAIL) { 486 mAlert.setIcon(mAlert.getIconAttributeResId(android.R.attr.alertDialogIcon)); 487 mAlert.getButton(DialogInterface.BUTTON_NEGATIVE).setVisibility(View.GONE); 488 mAlert.getButton(DialogInterface.BUTTON_POSITIVE) 489 .setText(getString(R.string.download_fail_ok)); 490 } else if (mWhichDialog == DIALOG_SEND_COMPLETE_SUCCESS) { 491 mAlert.getButton(DialogInterface.BUTTON_NEGATIVE).setVisibility(View.GONE); 492 mAlert.getButton(DialogInterface.BUTTON_POSITIVE) 493 .setText(getString(R.string.upload_succ_ok)); 494 } else if (mWhichDialog == DIALOG_SEND_COMPLETE_FAIL) { 495 mAlert.setIcon(mAlert.getIconAttributeResId(android.R.attr.alertDialogIcon)); 496 mAlert.getButton(DialogInterface.BUTTON_POSITIVE) 497 .setText(getString(R.string.upload_fail_ok)); 498 mAlert.getButton(DialogInterface.BUTTON_NEGATIVE) 499 .setText(getString(R.string.upload_fail_cancel)); 500 } 501 } 502 } 503