1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25 package net.sf.bluecove.obex;
26
27 import java.awt.BorderLayout;
28 import java.awt.Dimension;
29 import java.awt.GridBagConstraints;
30 import java.awt.GridBagLayout;
31 import java.awt.Image;
32 import java.awt.Toolkit;
33 import java.awt.event.ActionEvent;
34 import java.awt.event.ActionListener;
35 import java.awt.event.MouseEvent;
36 import java.awt.event.MouseListener;
37 import java.io.ByteArrayOutputStream;
38 import java.io.File;
39 import java.io.FileInputStream;
40 import java.io.InputStream;
41 import java.net.URL;
42 import java.util.Enumeration;
43 import java.util.Hashtable;
44 import java.util.Iterator;
45 import java.util.List;
46 import java.util.Vector;
47
48 import javax.bluetooth.LocalDevice;
49 import javax.bluetooth.RemoteDevice;
50 import javax.swing.BorderFactory;
51 import javax.swing.Box;
52 import javax.swing.BoxLayout;
53 import javax.swing.ImageIcon;
54 import javax.swing.JButton;
55 import javax.swing.JComboBox;
56 import javax.swing.JFileChooser;
57 import javax.swing.JFrame;
58 import javax.swing.JLabel;
59 import javax.swing.JPanel;
60 import javax.swing.JProgressBar;
61 import javax.swing.SwingUtilities;
62 import javax.swing.Timer;
63 import javax.swing.border.EmptyBorder;
64
65
66
67
68 public class Main extends JFrame implements ActionListener, UserInteraction {
69
70 private static final long serialVersionUID = 1L;
71
72 private static final int BLUETOOTH_DISCOVERY_STD_SEC = 11;
73
74 private JLabel iconLabel;
75
76 private String status;
77
78 JProgressBar progressBar;
79
80 private ImageIcon btIcon;
81
82 private ImageIcon transferIcon;
83
84 private ImageIcon searchIcon;
85
86 private ImageIcon downloadIcon;
87
88 private JComboBox cbDevices;
89
90 private JButton btFindDevice;
91
92 private JButton btSend;
93
94 private JButton btCancel;
95
96 private BluetoothInquirer bluetoothInquirer;
97
98 private Hashtable devices = new Hashtable();
99
100 private JFileChooser fileChooser;
101
102 private String fileName;
103
104 private byte[] data;
105
106 private List queue = new Vector();
107
108 protected Main() {
109 super("BlueCove OBEX Push");
110 this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
111
112 Image btImage = Toolkit.getDefaultToolkit().getImage(Main.class.getResource("/icon.png"));
113 btIcon = new ImageIcon(btImage);
114 transferIcon = new ImageIcon((Toolkit.getDefaultToolkit().getImage(Main.class.getResource("/transfer.png"))));
115 searchIcon = new ImageIcon((Toolkit.getDefaultToolkit().getImage(Main.class.getResource("/search.png"))));
116 downloadIcon = new ImageIcon((Toolkit.getDefaultToolkit().getImage(Main.class.getResource("/download.png"))));
117
118 this.setIconImage(btImage);
119
120 JPanel contentPane = (JPanel) this.getContentPane();
121 contentPane.setLayout(new BorderLayout(10, 10));
122 contentPane.setBorder(new EmptyBorder(10, 10, 10, 10));
123 contentPane.setTransferHandler(new DropTransferHandler(this));
124
125 contentPane.addMouseListener(new MouseDoubleClickListener());
126
127 JPanel progressPanel = new JPanel();
128 progressPanel.setLayout(new GridBagLayout());
129 GridBagConstraints c = new GridBagConstraints();
130
131 iconLabel = new JLabel();
132 iconLabel.setIcon(btIcon);
133 c.fill = GridBagConstraints.BOTH;
134 c.weightx = 1.0;
135 progressPanel.add(iconLabel, c);
136
137 progressBar = new JProgressBar(0, 100);
138 progressBar.setValue(0);
139 progressBar.setStringPainted(true);
140
141 c.fill = GridBagConstraints.HORIZONTAL;
142 c.gridwidth = GridBagConstraints.REMAINDER;
143 progressPanel.add(progressBar, c);
144
145 getContentPane().add(progressPanel, BorderLayout.NORTH);
146
147 JPanel optionsPanel = new JPanel();
148
149 JLabel deviceLabel = new JLabel("Send to:");
150 optionsPanel.add(deviceLabel);
151 cbDevices = new JComboBox();
152 cbDevices.addItem("{no device found}");
153 cbDevices.setEnabled(false);
154 optionsPanel.add(cbDevices);
155 optionsPanel.add(btFindDevice = new JButton("Find"));
156 btFindDevice.addActionListener(this);
157
158 getContentPane().add(optionsPanel, BorderLayout.CENTER);
159
160 JPanel actionPanel = new JPanel();
161 actionPanel.setLayout(new BoxLayout(actionPanel, BoxLayout.LINE_AXIS));
162 actionPanel.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 5));
163 actionPanel.add(Box.createHorizontalGlue());
164 actionPanel.add(btSend = new JButton("Send"));
165 btSend.addActionListener(this);
166 actionPanel.add(Box.createRigidArea(new Dimension(10, 0)));
167 actionPanel.add(btCancel = new JButton("Cancel"));
168 btCancel.addActionListener(this);
169
170 contentPane.add(actionPanel, BorderLayout.SOUTH);
171 btSend.setEnabled(false);
172 String selected = Persistence.loadDevices(devices);
173 updateDevices(selected);
174 }
175
176 private static void createAndShowGUI(final String[] args) {
177 final Main app = new Main();
178 app.pack();
179 app.center();
180 app.setVisible(true);
181 SwingUtilities.invokeLater(new Runnable() {
182 public void run() {
183 if (app.initializeBlueCove()) {
184 if (args.length != 0) {
185 app.downloadFile(args[0]);
186 }
187 }
188 }
189 });
190 }
191
192 public static void main(final String[] args) {
193 SwingUtilities.invokeLater(new Runnable() {
194 public void run() {
195 createAndShowGUI(args);
196 }
197 });
198 }
199
200 private void center() {
201 Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
202 this.setLocation(((screenSize.width - this.getWidth()) / 2), ((screenSize.height - this.getHeight()) / 2));
203 }
204
205 public void showStatus(final String message) {
206 setStatus(message);
207 }
208
209 protected void setStatus(final String message) {
210 status = message;
211 progressBar.setString(message);
212 }
213
214 public void setProgressMaximum(int n) {
215 progressBar.setMaximum(n);
216 }
217
218 public void setProgressValue(int n) {
219 progressBar.setValue(n);
220 SwingUtilities.invokeLater(new Runnable() {
221 public void run() {
222 progressBar.setString(status);
223 }
224 });
225 }
226
227 public void setProgressDone() {
228 progressBar.setValue(0);
229 }
230
231 protected void disabledBluetooth() {
232 btFindDevice.setEnabled(false);
233 cbDevices.setEnabled(false);
234 setStatus("BlueCove not avalable");
235 btSend.setEnabled(false);
236 iconLabel.setIcon(new ImageIcon((Toolkit.getDefaultToolkit().getImage(Main.class.getResource("/bt-off.png")))));
237 }
238
239 protected boolean initializeBlueCove() {
240 try {
241 LocalDevice localDevice = LocalDevice.getLocalDevice();
242 if ("000000000000".equals(localDevice.getBluetoothAddress())) {
243 throw new Exception();
244 }
245 bluetoothInquirer = new BluetoothInquirer(this);
246 setStatus("BlueCove Ready");
247 return true;
248 } catch (Throwable e) {
249 Logger.error(e);
250 disabledBluetooth();
251 return false;
252 }
253 }
254
255 public void actionPerformed(ActionEvent e) {
256 if (e.getSource() == btFindDevice) {
257 bluetoothDiscovery();
258 } else if (e.getSource() == btCancel) {
259 shutdown();
260 System.exit(0);
261 } else if (e.getSource() == btSend) {
262 obexSend();
263 }
264 }
265
266 private class MouseDoubleClickListener implements MouseListener {
267
268 private long firstClick = 0;
269
270 public void mouseClicked(MouseEvent e) {
271 long now = System.currentTimeMillis();
272 if ((firstClick != 0) && (firstClick - now < 1000)) {
273 fireDoubleClick();
274 } else {
275 firstClick = now;
276 }
277
278 }
279
280 public void mouseEntered(MouseEvent e) {
281 firstClick = 0;
282 }
283
284 public void mouseExited(MouseEvent e) {
285 firstClick = 0;
286 }
287
288 public void mousePressed(MouseEvent e) {
289 }
290
291 public void mouseReleased(MouseEvent e) {
292 }
293
294 }
295
296 public void fireDoubleClick() {
297 if (fileChooser == null) {
298 fileChooser = new JFileChooser();
299 fileChooser.setDialogTitle("Select File to send...");
300 fileChooser.setCurrentDirectory(new File(Persistence.getProperty("recentDirectory", ".")));
301 }
302 int returnVal = fileChooser.showOpenDialog(Main.this);
303 if (returnVal == JFileChooser.APPROVE_OPTION) {
304 Persistence.setProperty("recentDirectory", fileChooser.getCurrentDirectory().getAbsolutePath());
305 downloadFile(DropTransferHandler.getCanonicalFileURL(fileChooser.getSelectedFile()));
306 saveConfig();
307 }
308
309 }
310
311 private void selectNextFile() {
312 if (queue.size() > 0) {
313 String url = (String) queue.remove(0);
314 downloadFile(url);
315 }
316 }
317
318 public void queueFile(String url) {
319 queue.add(url);
320 }
321
322 private void saveConfig() {
323 Persistence.storeDevices(devices, getSelectedDeviceAddress());
324 }
325
326 private class DiscoveryTimerListener implements ActionListener {
327 int seconds = 0;
328
329 public void actionPerformed(ActionEvent e) {
330 if (seconds < BLUETOOTH_DISCOVERY_STD_SEC) {
331 seconds++;
332 setProgressValue(seconds);
333 }
334 }
335 }
336
337 private void addDevice(String btAddress, String name, String obexUrl) {
338 String key = btAddress.toLowerCase();
339 DeviceInfo di = (DeviceInfo) devices.get(key);
340 if (di == null) {
341 di = new DeviceInfo();
342 }
343 di.btAddress = btAddress;
344
345 if (di.name == null) {
346 di.name = name;
347 } else if (btAddress.equals(di.name)) {
348 di.name = name;
349 }
350 di.obexUrl = obexUrl;
351 di.obexServiceFound = true;
352 devices.put(key, di);
353 }
354
355 private void updateDevices(String selected) {
356 cbDevices.removeAllItems();
357 if (devices.size() == 0) {
358 cbDevices.addItem("{no device found}");
359 btSend.setEnabled(false);
360 cbDevices.setEnabled(false);
361 } else {
362 for (Enumeration i = devices.keys(); i.hasMoreElements();) {
363 String addr = (String) i.nextElement();
364 DeviceInfo di = (DeviceInfo) devices.get(addr);
365 cbDevices.addItem(di);
366 if ((selected != null) && (selected.equals(di.btAddress))) {
367 cbDevices.setSelectedItem(di);
368 }
369 }
370 cbDevices.setEnabled(true);
371 btSend.setEnabled(true);
372 }
373 }
374
375 private void bluetoothDiscovery() {
376 final Timer timer = new Timer(1000, new DiscoveryTimerListener());
377 progressBar.setMaximum(BLUETOOTH_DISCOVERY_STD_SEC);
378 setProgressValue(0);
379 Thread t = new Thread() {
380 public void run() {
381 if (bluetoothInquirer.startInquiry()) {
382 iconLabel.setIcon(searchIcon);
383 setStatus("Bluetooth discovery started");
384 btFindDevice.setEnabled(false);
385 btSend.setEnabled(false);
386 timer.start();
387 while (bluetoothInquirer.inquiring) {
388 try {
389 Thread.sleep(1000);
390 } catch (Exception e) {
391 }
392 }
393 timer.stop();
394
395
396 setProgressValue(0);
397 int idx = 0;
398 progressBar.setMaximum(bluetoothInquirer.devices.size());
399 for (Iterator iter = bluetoothInquirer.devices.iterator(); iter.hasNext();) {
400 RemoteDevice dev = (RemoteDevice) iter.next();
401 String obexUrl = bluetoothInquirer.findOBEX(dev.getBluetoothAddress());
402 if (obexUrl != null) {
403 Logger.debug("found obex url", obexUrl);
404 addDevice(dev.getBluetoothAddress(), BluetoothInquirer.getFriendlyName(dev), obexUrl);
405 }
406 idx++;
407 setProgressValue(idx);
408 }
409 setProgressValue(0);
410 saveConfig();
411 updateDevices(null);
412 btFindDevice.setEnabled(true);
413 btSend.setEnabled(true);
414 iconLabel.setIcon(btIcon);
415 }
416 }
417 };
418 t.start();
419 }
420
421 private String blueSoleilFindOBEX(String btAddress, String obexUrl) {
422 if ("bluesoleil".equals(LocalDevice.getProperty("bluecove.stack"))) {
423 RemoteDevice dev = new RemoteDeviceExt(btAddress);
424 String foundObexUrl = bluetoothInquirer.findOBEX(dev.getBluetoothAddress());
425 if (foundObexUrl != null) {
426 Logger.debug("found", btAddress);
427 addDevice(dev.getBluetoothAddress(), BluetoothInquirer.getFriendlyName(dev), foundObexUrl);
428 }
429 return foundObexUrl;
430 }
431 return obexUrl;
432 }
433
434 private DeviceInfo getSelectedDevice() {
435 Object o = cbDevices.getSelectedItem();
436 if ((o == null) || !(o instanceof DeviceInfo)) {
437 return null;
438 }
439 return (DeviceInfo) o;
440 }
441
442 private String getSelectedDeviceAddress() {
443 DeviceInfo d = getSelectedDevice();
444 if (d == null) {
445 return null;
446 }
447 return d.btAddress;
448 }
449
450 private void obexSend() {
451 if (fileName == null) {
452 setStatus("No file selected");
453 return;
454 }
455 final DeviceInfo d = getSelectedDevice();
456 if (d == null) {
457 setStatus("No Device selected");
458 return;
459 }
460 final ObexBluetoothClient o = new ObexBluetoothClient(this, fileName, data);
461 Thread t = new Thread() {
462 public void run() {
463 btSend.setEnabled(false);
464 iconLabel.setIcon(transferIcon);
465 String obexUrl = d.obexUrl;
466 if (!d.obexServiceFound) {
467 obexUrl = blueSoleilFindOBEX(d.btAddress, obexUrl);
468 }
469 if (obexUrl != null) {
470 if (o.obexPut(obexUrl)) {
471 selectNextFile();
472 }
473 } else {
474 setStatus("Service not found");
475 }
476 btSend.setEnabled(true);
477 iconLabel.setIcon(btIcon);
478 saveConfig();
479 }
480
481 };
482 t.start();
483 }
484
485 private static String simpleFileName(String filePath) {
486 int idx = filePath.lastIndexOf('/');
487 if (idx == -1) {
488 idx = filePath.lastIndexOf('\\');
489 }
490 if (idx == -1) {
491 return filePath;
492 }
493 return filePath.substring(idx + 1);
494 }
495
496 void downloadFile(final String filePath) {
497 Thread t = new Thread() {
498 public void run() {
499 InputStream is = null;
500 try {
501 iconLabel.setIcon(downloadIcon);
502 String path = filePath;
503 String inputFileName;
504 File file = new File(filePath);
505 if (file.exists()) {
506 is = new FileInputStream(file);
507 inputFileName = file.getName();
508 } else {
509 URL url = new URL(path);
510 is = url.openConnection().getInputStream();
511 inputFileName = url.getFile();
512 }
513 ByteArrayOutputStream bos = new ByteArrayOutputStream();
514 byte[] buffer = new byte[0xFF];
515 int i = is.read(buffer);
516 int done = 0;
517 while (i != -1) {
518 bos.write(buffer, 0, i);
519 done += i;
520
521 i = is.read(buffer);
522 }
523 data = bos.toByteArray();
524 fileName = simpleFileName(inputFileName);
525 setStatus((data.length / 1024) + "k " + fileName);
526 } catch (Throwable e) {
527 Logger.error(e);
528 setStatus("Download error " + e.getMessage());
529 } finally {
530 IOUtils.closeQuietly(is);
531 iconLabel.setIcon(btIcon);
532 }
533 }
534 };
535 t.start();
536
537 }
538
539 private void shutdown() {
540 if (bluetoothInquirer != null) {
541 bluetoothInquirer.shutdown();
542 bluetoothInquirer = null;
543 }
544 }
545 }