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