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