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 BluetoothOppUtility.retryTransfer(this, mTransInfo); 375 376 BluetoothDevice remoteDevice = mAdapter.getRemoteDevice(mTransInfo.mDestAddr); 377 378 // Display toast message 379 Toast.makeText( 380 this, 381 this.getString(R.string.bt_toast_4, BluetoothOppManager.getInstance( 382 this).getDeviceName(remoteDevice)), Toast.LENGTH_SHORT) 383 .show(); 384 385 } else if (mWhichDialog == DIALOG_SEND_COMPLETE_SUCCESS) { 386 BluetoothOppUtility.updateVisibilityToHidden(this, mUri); 387 ((NotificationManager)getSystemService(NOTIFICATION_SERVICE)) 388 .cancel(mTransInfo.mID); 389 } 390 break; 391 392 case DialogInterface.BUTTON_NEGATIVE: 393 if (mWhichDialog == DIALOG_RECEIVE_ONGOING || mWhichDialog == DIALOG_SEND_ONGOING) { 394 // "Stop" button 395 this.getContentResolver().delete(mUri, null, null); 396 397 String msg = ""; 398 if (mWhichDialog == DIALOG_RECEIVE_ONGOING) { 399 msg = getString(R.string.bt_toast_3, mTransInfo.mDeviceName); 400 } else if (mWhichDialog == DIALOG_SEND_ONGOING) { 401 msg = getString(R.string.bt_toast_6, mTransInfo.mDeviceName); 402 } 403 Toast.makeText(this, msg, Toast.LENGTH_SHORT).show(); 404 405 ((NotificationManager)getSystemService(NOTIFICATION_SERVICE)) 406 .cancel(mTransInfo.mID); 407 } else if (mWhichDialog == DIALOG_SEND_COMPLETE_FAIL) { 408 409 BluetoothOppUtility.updateVisibilityToHidden(this, mUri); 410 } 411 break; 412 } 413 finish(); 414 } 415 416 /** 417 * Update progress bar per data got from content provider 418 */ 419 private void updateProgressbar() { 420 mTransInfo = BluetoothOppUtility.queryRecord(this, mUri); 421 if (mTransInfo == null) { 422 if (V) Log.e(TAG, "Error: Can not get data from db"); 423 return; 424 } 425 426 if (mTransInfo.mTotalBytes == 0) { 427 // if Max and progress both equal 0, the progress display 100%. 428 // Below is to fix it. 429 mProgressTransfer.setMax(100); 430 } else { 431 mProgressTransfer.setMax(mTransInfo.mTotalBytes); 432 } 433 434 mProgressTransfer.setProgress(mTransInfo.mCurrentBytes); 435 436 mPercentView.setText(BluetoothOppUtility.formatProgressText(mTransInfo.mTotalBytes, 437 mTransInfo.mCurrentBytes)); 438 439 // Handle the case when DIALOG_RECEIVE_ONGOING evolve to 440 // DIALOG_RECEIVE_COMPLETE_SUCCESS/DIALOG_RECEIVE_COMPLETE_FAIL 441 // Handle the case when DIALOG_SEND_ONGOING evolve to 442 // DIALOG_SEND_COMPLETE_SUCCESS/DIALOG_SEND_COMPLETE_FAIL 443 if (!mIsComplete && BluetoothShare.isStatusCompleted(mTransInfo.mStatus) 444 && mNeedUpdateButton) { 445 displayWhichDialog(); 446 updateButton(); 447 customizeViewContent(); 448 } 449 } 450 451 /** 452 * Update button when one transfer goto complete from ongoing 453 */ 454 private void updateButton() { 455 if (mWhichDialog == DIALOG_RECEIVE_COMPLETE_SUCCESS) { 456 mAlert.getButton(DialogInterface.BUTTON_NEGATIVE).setVisibility(View.GONE); 457 mAlert.getButton(DialogInterface.BUTTON_POSITIVE).setText( 458 getString(R.string.download_succ_ok)); 459 } else if (mWhichDialog == DIALOG_RECEIVE_COMPLETE_FAIL) { 460 mAlert.setIcon(mAlert.getIconAttributeResId(android.R.attr.alertDialogIcon)); 461 mAlert.getButton(DialogInterface.BUTTON_NEGATIVE).setVisibility(View.GONE); 462 mAlert.getButton(DialogInterface.BUTTON_POSITIVE).setText( 463 getString(R.string.download_fail_ok)); 464 } else if (mWhichDialog == DIALOG_SEND_COMPLETE_SUCCESS) { 465 mAlert.getButton(DialogInterface.BUTTON_NEGATIVE).setVisibility(View.GONE); 466 mAlert.getButton(DialogInterface.BUTTON_POSITIVE).setText( 467 getString(R.string.upload_succ_ok)); 468 } else if (mWhichDialog == DIALOG_SEND_COMPLETE_FAIL) { 469 mAlert.setIcon(mAlert.getIconAttributeResId(android.R.attr.alertDialogIcon)); 470 mAlert.getButton(DialogInterface.BUTTON_POSITIVE).setText( 471 getString(R.string.upload_fail_ok)); 472 mAlert.getButton(DialogInterface.BUTTON_NEGATIVE).setText( 473 getString(R.string.upload_fail_cancel)); 474 } 475 } 476 } 477