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