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