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