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.app.Activity;
38import android.app.AlertDialog;
39import android.bluetooth.BluetoothAdapter;
40import android.content.DialogInterface;
41import android.content.Intent;
42import android.database.Cursor;
43import android.net.Uri;
44import android.os.Bundle;
45import android.util.Log;
46import android.view.ContextMenu;
47import android.view.ContextMenu.ContextMenuInfo;
48import android.view.Menu;
49import android.view.MenuInflater;
50import android.view.MenuItem;
51import android.view.View;
52import android.widget.AdapterView;
53import android.widget.AdapterView.OnItemClickListener;
54import android.widget.ListView;
55
56/**
57 * View showing the user's finished bluetooth opp transfers that the user does
58 * not confirm. Including outbound and inbound transfers, both successful and
59 * failed. *
60 */
61public class BluetoothOppTransferHistory extends Activity implements
62        View.OnCreateContextMenuListener, OnItemClickListener {
63    private static final String TAG = "BluetoothOppTransferHistory";
64
65    private static final boolean V = Constants.VERBOSE;
66
67    private ListView mListView;
68
69    private Cursor mTransferCursor;
70
71    private BluetoothOppTransferAdapter mTransferAdapter;
72
73    private int mIdColumnId;
74
75    private int mContextMenuPosition;
76
77    private boolean mShowAllIncoming;
78
79    private boolean mContextMenu = false;
80
81    /** Class to handle Notification Manager updates */
82    private BluetoothOppNotification mNotifier;
83
84    @Override
85    public void onCreate(Bundle icicle) {
86        super.onCreate(icicle);
87        setContentView(R.layout.bluetooth_transfers_page);
88        mListView = (ListView)findViewById(R.id.list);
89        mListView.setEmptyView(findViewById(R.id.empty));
90
91        mShowAllIncoming = getIntent().getBooleanExtra(
92                Constants.EXTRA_SHOW_ALL_FILES, false);
93
94        String direction;
95        int dir = getIntent().getIntExtra("direction", 0);
96        if (dir == BluetoothShare.DIRECTION_OUTBOUND) {
97            setTitle(getText(R.string.outbound_history_title));
98            direction = "(" + BluetoothShare.DIRECTION + " == " + BluetoothShare.DIRECTION_OUTBOUND
99                    + ")";
100        } else {
101            if (mShowAllIncoming) {
102                setTitle(getText(R.string.btopp_live_folder));
103            } else {
104                setTitle(getText(R.string.inbound_history_title));
105            }
106            direction = "(" + BluetoothShare.DIRECTION + " == " + BluetoothShare.DIRECTION_INBOUND
107                    + ")";
108        }
109
110        String selection = BluetoothShare.STATUS + " >= '200' AND " + direction;
111
112        if (!mShowAllIncoming) {
113            selection = selection + " AND ("
114                    + BluetoothShare.VISIBILITY + " IS NULL OR "
115                    + BluetoothShare.VISIBILITY + " == '"
116                    + BluetoothShare.VISIBILITY_VISIBLE + "')";
117        }
118
119        final String sortOrder = BluetoothShare.TIMESTAMP + " DESC";
120
121        mTransferCursor = managedQuery(BluetoothShare.CONTENT_URI, new String[] {
122                "_id", BluetoothShare.FILENAME_HINT, BluetoothShare.STATUS,
123                BluetoothShare.TOTAL_BYTES, BluetoothShare._DATA, BluetoothShare.TIMESTAMP,
124                BluetoothShare.VISIBILITY, BluetoothShare.DESTINATION, BluetoothShare.DIRECTION
125        }, selection, sortOrder);
126
127        // only attach everything to the listbox if we can access
128        // the transfer database. Otherwise, just show it empty
129        if (mTransferCursor != null) {
130            mIdColumnId = mTransferCursor.getColumnIndexOrThrow(BluetoothShare._ID);
131            // Create a list "controller" for the data
132            mTransferAdapter = new BluetoothOppTransferAdapter(this,
133                    R.layout.bluetooth_transfer_item, mTransferCursor);
134            mListView.setAdapter(mTransferAdapter);
135            mListView.setScrollBarStyle(View.SCROLLBARS_INSIDE_INSET);
136            mListView.setOnCreateContextMenuListener(this);
137            mListView.setOnItemClickListener(this);
138        }
139
140        mNotifier = new BluetoothOppNotification(this);
141        mContextMenu = false;
142    }
143
144    @Override
145    public boolean onCreateOptionsMenu(Menu menu) {
146        if (mTransferCursor != null && !mShowAllIncoming) {
147            MenuInflater inflater = getMenuInflater();
148            inflater.inflate(R.menu.transferhistory, menu);
149        }
150        return true;
151    }
152
153    @Override
154    public boolean onPrepareOptionsMenu(Menu menu) {
155        if (!mShowAllIncoming) {
156            boolean showClear = getClearableCount() > 0;
157            menu.findItem(R.id.transfer_menu_clear_all).setEnabled(showClear);
158        }
159        return super.onPrepareOptionsMenu(menu);
160    }
161
162    @Override
163    public boolean onOptionsItemSelected(MenuItem item) {
164        switch (item.getItemId()) {
165            case R.id.transfer_menu_clear_all:
166                promptClearList();
167                return true;
168        }
169        return false;
170    }
171
172    @Override
173    public boolean onContextItemSelected(MenuItem item) {
174        if (mTransferCursor.getCount() == 0) {
175            Log.i(TAG, "History is already cleared, not clearing again");
176            return true;
177        }
178        mTransferCursor.moveToPosition(mContextMenuPosition);
179        switch (item.getItemId()) {
180            case R.id.transfer_menu_open:
181                openCompleteTransfer();
182                updateNotificationWhenBtDisabled();
183                return true;
184
185            case R.id.transfer_menu_clear:
186                int sessionId = mTransferCursor.getInt(mIdColumnId);
187                Uri contentUri = Uri.parse(BluetoothShare.CONTENT_URI + "/" + sessionId);
188                BluetoothOppUtility.updateVisibilityToHidden(this, contentUri);
189                updateNotificationWhenBtDisabled();
190                return true;
191        }
192        return false;
193    }
194
195    @Override
196    public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
197        if (mTransferCursor != null) {
198            mContextMenu = true;
199            AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo)menuInfo;
200            mTransferCursor.moveToPosition(info.position);
201            mContextMenuPosition = info.position;
202
203            String fileName = mTransferCursor.getString(mTransferCursor
204                    .getColumnIndexOrThrow(BluetoothShare.FILENAME_HINT));
205            if (fileName == null) {
206                fileName = this.getString(R.string.unknown_file);
207            }
208            menu.setHeaderTitle(fileName);
209
210            MenuInflater inflater = getMenuInflater();
211            if (mShowAllIncoming) {
212                inflater.inflate(R.menu.receivedfilescontextfinished, menu);
213            } else {
214                inflater.inflate(R.menu.transferhistorycontextfinished, menu);
215            }
216        }
217    }
218
219    /**
220     * Prompt the user if they would like to clear the transfer history
221     */
222    private void promptClearList() {
223        new AlertDialog.Builder(this).setTitle(R.string.transfer_clear_dlg_title).setMessage(
224                R.string.transfer_clear_dlg_msg).setPositiveButton(android.R.string.ok,
225                new DialogInterface.OnClickListener() {
226                    public void onClick(DialogInterface dialog, int whichButton) {
227                        clearAllDownloads();
228                    }
229                }).setNegativeButton(android.R.string.cancel, null).show();
230    }
231
232    /**
233     * Get the number of finished transfers, including error and success.
234     */
235    private int getClearableCount() {
236        int count = 0;
237        if (mTransferCursor.moveToFirst()) {
238            while (!mTransferCursor.isAfterLast()) {
239                int statusColumnId = mTransferCursor.getColumnIndexOrThrow(BluetoothShare.STATUS);
240                int status = mTransferCursor.getInt(statusColumnId);
241                if (BluetoothShare.isStatusCompleted(status)) {
242                    count++;
243                }
244                mTransferCursor.moveToNext();
245            }
246        }
247        return count;
248    }
249
250    /**
251     * Clear all finished transfers, error and success transfer items.
252     */
253    private void clearAllDownloads() {
254        if (mTransferCursor.moveToFirst()) {
255            while (!mTransferCursor.isAfterLast()) {
256                int sessionId = mTransferCursor.getInt(mIdColumnId);
257                Uri contentUri = Uri.parse(BluetoothShare.CONTENT_URI + "/" + sessionId);
258                BluetoothOppUtility.updateVisibilityToHidden(this, contentUri);
259
260                mTransferCursor.moveToNext();
261            }
262            updateNotificationWhenBtDisabled();
263        }
264    }
265
266    /*
267     * (non-Javadoc)
268     * @see
269     * android.widget.AdapterView.OnItemClickListener#onItemClick(android.widget
270     * .AdapterView, android.view.View, int, long)
271     */
272    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
273        // Open the selected item
274        if (V) Log.v(TAG, "onItemClick: ContextMenu = " + mContextMenu);
275        if (!mContextMenu) {
276            mTransferCursor.moveToPosition(position);
277            openCompleteTransfer();
278            updateNotificationWhenBtDisabled();
279        }
280        mContextMenu = false;
281    }
282
283    /**
284     * Open the selected finished transfer. mDownloadCursor must be moved to
285     * appropriate position before calling this function
286     */
287    private void openCompleteTransfer() {
288        int sessionId = mTransferCursor.getInt(mIdColumnId);
289        Uri contentUri = Uri.parse(BluetoothShare.CONTENT_URI + "/" + sessionId);
290        BluetoothOppTransferInfo transInfo = BluetoothOppUtility.queryRecord(this, contentUri);
291        if (transInfo == null) {
292            Log.e(TAG, "Error: Can not get data from db");
293            return;
294        }
295        if (transInfo.mDirection == BluetoothShare.DIRECTION_INBOUND
296                && BluetoothShare.isStatusSuccess(transInfo.mStatus)) {
297            // if received file successfully, open this file
298            BluetoothOppUtility.updateVisibilityToHidden(this, contentUri);
299            BluetoothOppUtility.openReceivedFile(this, transInfo.mFileName, transInfo.mFileType,
300                    transInfo.mTimeStamp, contentUri);
301        } else {
302            Intent in = new Intent(this, BluetoothOppTransferActivity.class);
303            in.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
304            in.setDataAndNormalize(contentUri);
305            this.startActivity(in);
306        }
307    }
308
309    /**
310     * When Bluetooth is disabled, notification can not be updated by
311     * ContentObserver in OppService, so need update manually.
312     */
313    private void updateNotificationWhenBtDisabled() {
314        BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
315        if (!adapter.isEnabled()) {
316            if (V) Log.v(TAG, "Bluetooth is not enabled, update notification manually.");
317            mNotifier.updateNotification();
318        }
319    }
320}
321