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