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