fixed some NullPointer bugs and changed storage type for BluetoothDevices
[hqbeamer.git] / src / de / bongo / android / hqbeamer / HQBeamerActivity.java
1 package de.bongo.android.hqbeamer;
2
3 import java.io.IOException;
4 import java.io.InputStream;
5 import java.io.OutputStream;
6 import java.lang.reflect.InvocationTargetException;
7 import java.lang.reflect.Method;
8 import java.util.ArrayList;
9 import java.util.Enumeration;
10 import java.util.Hashtable;
11 import java.util.Iterator;
12 import java.util.Set;
13
14 import android.app.Activity;
15 import android.app.AlertDialog;
16 import android.app.Dialog;
17 import android.app.ProgressDialog;
18 import android.bluetooth.BluetoothAdapter;
19 import android.bluetooth.BluetoothDevice;
20 import android.bluetooth.BluetoothSocket;
21 import android.content.BroadcastReceiver;
22 import android.content.Context;
23 import android.content.DialogInterface;
24 import android.content.Intent;
25 import android.content.IntentFilter;
26 import android.os.Bundle;
27 import android.os.Handler;
28 import android.os.Message;
29 import android.util.Log;
30 import android.widget.CompoundButton;
31 import android.widget.Toast;
32 import android.widget.CompoundButton.OnCheckedChangeListener;
33 import android.widget.ToggleButton;
34
35 public class HQBeamerActivity extends Activity {
36     ToggleButton powerToggle;
37         private static final int REQUEST_ENABLE_BT = 0;
38         private static final int CHOOSE_DEVICE_DIALOG = 0;
39         private static final int WARN_CHOOSE_DEVICE = 1;
40         private Boolean isBluetoothEnabled = false;
41         private BluetoothAdapter mBluetoothAdapter;
42         private ArrayList<BluetoothDevice> availableDevices;
43         private int indexDeviceChosen;
44         private ConnectThread connectThread;
45         private ConnectedThread connectedThread;
46         private BroadcastReceiver mReceiver;
47         /* BEGIN Bluetooth / Beamer Messages */
48                 /* Begin IDs */
49                 private static final int MESSAGE_READ = 0;
50                 private static final int MESSAGE_WRITE = 1;
51                 /* end IDs */
52         private static final byte[] POWER_ON = {0x21};
53         private static final byte[] POWER_OFF = {0x22};
54         /* END Bluetooth / Beamer Messages */
55         
56     @Override
57     public void onCreate(Bundle savedInstanceState) {
58         super.onCreate(savedInstanceState);
59         setContentView(R.layout.main);
60         powerToggle = (ToggleButton) findViewById(R.id.powerToggle);
61         
62         /* Start Bluetooth Initialization */
63         mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
64                 if (mBluetoothAdapter == null) {
65                         this.isBluetoothEnabled = false;
66                         new AlertDialog.Builder(this).setTitle("Error")
67                                            .setMessage("Your device seems not to support Bluetooth :(")
68                                            .setIcon(android.R.drawable.stat_notify_error)
69                                        .setCancelable(false)
70                                        .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
71                                            public void onClick(DialogInterface dialog, int id) {
72                                                    dialog.dismiss();
73                                            }
74                                        })
75                                 .create().show();
76                 }
77                 else {
78                         if (!mBluetoothAdapter.isEnabled()) {
79                             Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
80                             startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
81                         }
82                         else {
83                                 this.isBluetoothEnabled = true;
84                         }
85                 }
86                 /* Bluetooth is guaranteed to be avaliable now */
87         
88                 /* Prepare bluetooth connection */
89                 // Create a BroadcastReceiver for ACTION_FOUND
90             this.mReceiver = new BroadcastReceiver() {
91                     public void onReceive(Context context, Intent intent) {
92                         String action = intent.getAction();
93                         // When discovery finds a device
94                         if (BluetoothDevice.ACTION_FOUND.equals(action)) {
95                             // Get the BluetoothDevice object from the Intent
96                             BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
97                             // Add the name and address to an array adapter to show in a ListView
98                             availableDevices.add(device);
99                         }
100                     }
101                 };
102                 if (connectThread != null) {connectThread.cancel(); connectThread = null;}
103                 while(doBluetoothInquiry());
104         connectThread = new ConnectThread(availableDevices.get(indexDeviceChosen));
105
106                 
107                 /* UI Element Setup */
108                 this.powerToggle.setOnCheckedChangeListener(new OnCheckedChangeListener() {
109                         public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
110                                 connectedThread.write(POWER_ON);
111                         }
112                 });
113     }
114     
115     private boolean doBluetoothInquiry() {
116         this.availableDevices = new ArrayList<BluetoothDevice>();
117                 ProgressDialog dialog = ProgressDialog.show(this, "", 
118                 "Scanning for bluetooth devices...", true);
119                 dialog.show();
120                 Set<BluetoothDevice> pairedDevices = mBluetoothAdapter.getBondedDevices();
121                 // If there are paired devices
122                 if (pairedDevices.size() > 0) {
123                     // Loop through paired devices
124                     for (BluetoothDevice device : pairedDevices) {
125                         // Add the name and address to an array adapter to show in a ListView
126                         availableDevices.add(device);
127                     }
128                 }
129                 showDialog(CHOOSE_DEVICE_DIALOG);
130                 if(availableDevices.get(indexDeviceChosen)==null){ 
131                         availableDevices.clear();
132                         // user hasn't chosen any of the paired devs, trying to actively scan for some
133                         // Register the BroadcastReceiver
134                         IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
135                         registerReceiver(mReceiver, filter); // Don't forget to unregister during onDestroy
136                         showDialog(CHOOSE_DEVICE_DIALOG);
137                         if(availableDevices.get(indexDeviceChosen)==null){
138                                 return true;
139                         }
140                         else {
141                                 showDialog(WARN_CHOOSE_DEVICE);
142                                 return false;
143                         }
144                 }
145                 else {
146                         return true;
147                 }
148     } 
149     
150     private class ConnectThread extends Thread {
151         private final BluetoothSocket mmSocket;
152         private final BluetoothDevice mmDevice;
153      
154         public ConnectThread(BluetoothDevice device) {
155             // Use a temporary object that is later assigned to mmSocket,
156             // because mmSocket is final
157             BluetoothSocket tmp = null;
158             mmDevice = device;
159      
160             // Get a BluetoothSocket to connect with the given BluetoothDevice
161             try {
162                 // MY_UUID is the app's UUID string, also used by the server code
163                 //FIXME may be not avail in later vers better would be API >=10 http://stackoverflow.com/questions/5308373/how-to-create-insecure-rfcomm-socket-in-android
164                 Method m = device.getClass().getMethod("createInsecureRfcommSocket", new Class[] {int.class});
165                 tmp = (BluetoothSocket) m.invoke(device, 1);
166             } catch (SecurityException e) {
167                                 e.printStackTrace();
168                         } catch (NoSuchMethodException e) {
169                                 e.printStackTrace();
170                         } catch (IllegalArgumentException e) {
171                                 e.printStackTrace();
172                         } catch (IllegalAccessException e) {
173                                 e.printStackTrace();
174                         } catch (InvocationTargetException e) {
175                                 e.printStackTrace();
176                         }
177             mmSocket = tmp;
178         }
179      
180         public void run() {
181             // Cancel discovery because it will slow down the connection
182             mBluetoothAdapter.cancelDiscovery();
183      
184             try {
185                 // Connect the device through the socket. This will block
186                 // until it succeeds or throws an exception
187                 mmSocket.connect();
188             } catch (IOException connectException) {
189                 // Unable to connect; close the socket and get out
190                 try {
191                     mmSocket.close();
192                 } catch (IOException closeException) { }
193                 return;
194             }
195      
196             // Do work to manage the connection (in a separate thread)
197             manageConnectedSocket(mmSocket);
198         }
199      
200         /** Will cancel an in-progress connection, and close the socket */
201         public void cancel() {
202             try {
203                 mmSocket.close();
204             } catch (IOException e) { }
205         }
206     }
207     
208     private class ConnectedThread extends Thread {
209         private final BluetoothSocket mmSocket;
210         private final InputStream mmInStream;
211         private final OutputStream mmOutStream;
212      
213         public ConnectedThread(BluetoothSocket socket) {
214             mmSocket = socket;
215             InputStream tmpIn = null;
216             OutputStream tmpOut = null;
217      
218             // Get the input and output streams, using temp objects because
219             // member streams are final
220             try {
221                 tmpIn = socket.getInputStream();
222                 tmpOut = socket.getOutputStream();
223             } catch (IOException e) { }
224      
225             mmInStream = tmpIn;
226             mmOutStream = tmpOut;
227         }
228      
229         public void run() {
230             byte[] buffer = new byte[1024];  // buffer store for the stream
231             int bytes; // bytes returned from read()
232      
233             // Keep listening to the InputStream until an exception occurs
234             while (true) {
235                 try {
236                     // Read from the InputStream
237                     bytes = mmInStream.read(buffer);
238                     // Send the obtained bytes to the UI Activity
239                     mHandler.obtainMessage(MESSAGE_READ, bytes, -1, buffer)
240                             .sendToTarget();
241                 } catch (IOException e) {
242                     break;
243                 }
244             }
245         }
246      
247         /* Call this from the main Activity to send data to the remote device */
248         public void write(byte[] bytes) {
249             try {
250                 mmOutStream.write(bytes);
251             } catch (IOException e) { }
252         }
253      
254         /* Call this from the main Activity to shutdown the connection */
255         public void cancel() {
256             try {
257                 mmSocket.close();
258             } catch (IOException e) { }
259         }
260     }
261     
262     private final Handler mHandler = new Handler() {
263         @Override
264         public void handleMessage(Message msg) {
265             switch (msg.what) {
266             case MESSAGE_WRITE:
267                 byte[] writeBuf = (byte[]) msg.obj;
268                 break;
269             case MESSAGE_READ:
270                 byte[] readBuf = (byte[]) msg.obj;
271                 // FIXME check if correct code comes back
272                 break;
273             }
274         }
275     };
276
277     private void manageConnectedSocket(BluetoothSocket socket){
278         this.connectedThread = new ConnectedThread(socket);
279         connectedThread.start(); // FIXME run()?
280         // FIXME stub
281     }
282     
283 protected Dialog onCreateDialog(int id){
284         Dialog dialog;
285         switch(id){
286         case CHOOSE_DEVICE_DIALOG:
287                 ArrayList<String> strings = new ArrayList<String>();
288                 Iterator<BluetoothDevice> iter = availableDevices.iterator();
289                 while(iter.hasNext()){
290                         BluetoothDevice curBtDev = iter.next();
291                         strings.add(curBtDev.getName()+"\n"+curBtDev.getAddress());
292                 }
293                 AlertDialog.Builder builder = new AlertDialog.Builder(HQBeamerActivity.this);
294                 builder.setTitle("Pick a RFCOMM Device");
295                 builder.setItems(strings.toArray(new String[availableDevices.size()]),
296                                                                                  new DevChooserOnclickListener(strings));
297                 dialog = builder.create();
298                 return dialog;
299         case WARN_CHOOSE_DEVICE:
300                 dialog = new AlertDialog.Builder(HQBeamerActivity.this)
301                                                                 .setTitle("Warning")
302                                                                 .setMessage("It's required to choose a device!")
303                                                                 .setPositiveButton("Rescan", new DialogInterface.OnClickListener() {
304                                                                                 @Override
305                                                                                 public void onClick(DialogInterface arg0, int arg1) {
306                                                                                         doBluetoothInquiry();                                                                                   
307                                                                                 }
308                                                                         }).create();
309                 
310                 return dialog;
311         default:
312                 return null;
313         }
314     }
315         
316         private class DevChooserOnclickListener implements DialogInterface.OnClickListener{
317                 ArrayList<String> strings;
318                 public DevChooserOnclickListener(ArrayList<String> strings){
319                         this.strings = strings;
320                 }
321             public void onClick(DialogInterface dialog, int item) {
322                 HQBeamerActivity.this.indexDeviceChosen = item;
323             }
324         }
325     
326     protected void onActivityResult(int requestCode, int resultCode, Intent data){
327         if(requestCode == REQUEST_ENABLE_BT){
328                 if(resultCode != RESULT_OK){
329                         new AlertDialog.Builder(this).setTitle("Error")
330                                    .setMessage("Bluetooth not enabled")
331                                    .setIcon(android.R.drawable.stat_notify_error)
332                                .setCancelable(false)
333                                .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
334                                    public void onClick(DialogInterface dialog, int id) {
335                                            dialog.dismiss();
336                                    }
337                                })
338                                .setPositiveButton("Retry", new DialogInterface.OnClickListener() {
339                                    public void onClick(DialogInterface dialog, int id) {
340                                                         if (!mBluetoothAdapter.isEnabled()) {
341                                                                 HQBeamerActivity.this.isBluetoothEnabled = true;
342                                                                 dialog.dismiss();
343                                                         }
344                                    }
345                                }).create().show();
346                 }
347         }       
348     }
349     
350     protected void onDestroy(){
351         unregisterReceiver(mReceiver);
352         super.onDestroy();
353     }
354 }