/** * BlueCove - Java library for Bluetooth * Copyright (C) 2006-2009 Vlad Skarzhevskyy * * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * * @author vlads * @version $Id$ */ package net.sf.bluecove.obex; import java.awt.BorderLayout; import java.awt.Dimension; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.Image; import java.awt.Toolkit; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.InputStream; import java.net.URL; import java.util.Enumeration; import java.util.Hashtable; import java.util.Iterator; import java.util.List; import java.util.Vector; import javax.bluetooth.LocalDevice; import javax.bluetooth.RemoteDevice; import javax.swing.BorderFactory; import javax.swing.Box; import javax.swing.BoxLayout; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JComboBox; import javax.swing.JFileChooser; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JProgressBar; import javax.swing.SwingUtilities; import javax.swing.Timer; import javax.swing.border.EmptyBorder; /** * */ public class Main extends JFrame implements ActionListener, UserInteraction { private static final long serialVersionUID = 1L; private static final int BLUETOOTH_DISCOVERY_STD_SEC = 11; private JLabel iconLabel; private String status; JProgressBar progressBar; private ImageIcon btIcon; private ImageIcon transferIcon; private ImageIcon searchIcon; private ImageIcon downloadIcon; private JComboBox cbDevices; private JButton btFindDevice; private JButton btSend; private JButton btCancel; private BluetoothInquirer bluetoothInquirer; private Hashtable devices = new Hashtable(); private JFileChooser fileChooser; private String fileName; private byte[] data; private List queue = new Vector(); protected Main() { super("BlueCove OBEX Push"); this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); Image btImage = Toolkit.getDefaultToolkit().getImage(Main.class.getResource("/icon.png")); btIcon = new ImageIcon(btImage); transferIcon = new ImageIcon((Toolkit.getDefaultToolkit().getImage(Main.class.getResource("/transfer.png")))); searchIcon = new ImageIcon((Toolkit.getDefaultToolkit().getImage(Main.class.getResource("/search.png")))); downloadIcon = new ImageIcon((Toolkit.getDefaultToolkit().getImage(Main.class.getResource("/download.png")))); this.setIconImage(btImage); JPanel contentPane = (JPanel) this.getContentPane(); contentPane.setLayout(new BorderLayout(10, 10)); contentPane.setBorder(new EmptyBorder(10, 10, 10, 10)); contentPane.setTransferHandler(new DropTransferHandler(this)); contentPane.addMouseListener(new MouseDoubleClickListener()); JPanel progressPanel = new JPanel(); progressPanel.setLayout(new GridBagLayout()); GridBagConstraints c = new GridBagConstraints(); iconLabel = new JLabel(); iconLabel.setIcon(btIcon); c.fill = GridBagConstraints.BOTH; c.weightx = 1.0; progressPanel.add(iconLabel, c); progressBar = new JProgressBar(0, 100); progressBar.setValue(0); progressBar.setStringPainted(true); c.fill = GridBagConstraints.HORIZONTAL; c.gridwidth = GridBagConstraints.REMAINDER; progressPanel.add(progressBar, c); getContentPane().add(progressPanel, BorderLayout.NORTH); JPanel optionsPanel = new JPanel(); JLabel deviceLabel = new JLabel("Send to:"); optionsPanel.add(deviceLabel); cbDevices = new JComboBox(); cbDevices.addItem("{no device found}"); cbDevices.setEnabled(false); optionsPanel.add(cbDevices); optionsPanel.add(btFindDevice = new JButton("Find")); btFindDevice.addActionListener(this); getContentPane().add(optionsPanel, BorderLayout.CENTER); JPanel actionPanel = new JPanel(); actionPanel.setLayout(new BoxLayout(actionPanel, BoxLayout.LINE_AXIS)); actionPanel.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 5)); actionPanel.add(Box.createHorizontalGlue()); actionPanel.add(btSend = new JButton("Send")); btSend.addActionListener(this); actionPanel.add(Box.createRigidArea(new Dimension(10, 0))); actionPanel.add(btCancel = new JButton("Cancel")); btCancel.addActionListener(this); contentPane.add(actionPanel, BorderLayout.SOUTH); btSend.setEnabled(false); String selected = Persistence.loadDevices(devices); updateDevices(selected); } private static void createAndShowGUI(final String[] args) { final Main app = new Main(); app.pack(); app.center(); app.setVisible(true); SwingUtilities.invokeLater(new Runnable() { public void run() { if (app.initializeBlueCove()) { if (args.length != 0) { app.downloadFile(args[0]); } } } }); } public static void main(final String[] args) { SwingUtilities.invokeLater(new Runnable() { public void run() { createAndShowGUI(args); } }); } private void center() { Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); this.setLocation(((screenSize.width - this.getWidth()) / 2), ((screenSize.height - this.getHeight()) / 2)); } public void showStatus(final String message) { setStatus(message); } protected void setStatus(final String message) { status = message; progressBar.setString(message); } public void setProgressMaximum(int n) { progressBar.setMaximum(n); } public void setProgressValue(int n) { progressBar.setValue(n); SwingUtilities.invokeLater(new Runnable() { public void run() { progressBar.setString(status); } }); } public void setProgressDone() { progressBar.setValue(0); } protected void disabledBluetooth() { btFindDevice.setEnabled(false); cbDevices.setEnabled(false); setStatus("BlueCove not avalable"); btSend.setEnabled(false); iconLabel.setIcon(new ImageIcon((Toolkit.getDefaultToolkit().getImage(Main.class.getResource("/bt-off.png"))))); } protected boolean initializeBlueCove() { try { LocalDevice localDevice = LocalDevice.getLocalDevice(); if ("000000000000".equals(localDevice.getBluetoothAddress())) { throw new Exception(); } bluetoothInquirer = new BluetoothInquirer(this); setStatus("BlueCove Ready"); return true; } catch (Throwable e) { Logger.error(e); disabledBluetooth(); return false; } } public void actionPerformed(ActionEvent e) { if (e.getSource() == btFindDevice) { bluetoothDiscovery(); } else if (e.getSource() == btCancel) { shutdown(); System.exit(0); } else if (e.getSource() == btSend) { obexSend(); } } private class MouseDoubleClickListener implements MouseListener { private long firstClick = 0; public void mouseClicked(MouseEvent e) { long now = System.currentTimeMillis(); if ((firstClick != 0) && (firstClick - now < 1000)) { fireDoubleClick(); } else { firstClick = now; } } public void mouseEntered(MouseEvent e) { firstClick = 0; } public void mouseExited(MouseEvent e) { firstClick = 0; } public void mousePressed(MouseEvent e) { } public void mouseReleased(MouseEvent e) { } } public void fireDoubleClick() { if (fileChooser == null) { fileChooser = new JFileChooser(); fileChooser.setDialogTitle("Select File to send..."); fileChooser.setCurrentDirectory(new File(Persistence.getProperty("recentDirectory", "."))); } int returnVal = fileChooser.showOpenDialog(Main.this); if (returnVal == JFileChooser.APPROVE_OPTION) { Persistence.setProperty("recentDirectory", fileChooser.getCurrentDirectory().getAbsolutePath()); downloadFile(DropTransferHandler.getCanonicalFileURL(fileChooser.getSelectedFile())); saveConfig(); } } private void selectNextFile() { if (queue.size() > 0) { String url = (String) queue.remove(0); downloadFile(url); } } public void queueFile(String url) { queue.add(url); } private void saveConfig() { Persistence.storeDevices(devices, getSelectedDeviceAddress()); } private class DiscoveryTimerListener implements ActionListener { int seconds = 0; public void actionPerformed(ActionEvent e) { if (seconds < BLUETOOTH_DISCOVERY_STD_SEC) { seconds++; setProgressValue(seconds); } } } private void addDevice(String btAddress, String name, String obexUrl) { String key = btAddress.toLowerCase(); DeviceInfo di = (DeviceInfo) devices.get(key); if (di == null) { di = new DeviceInfo(); } di.btAddress = btAddress; // Update name if one found if (di.name == null) { di.name = name; } else if (btAddress.equals(di.name)) { di.name = name; } di.obexUrl = obexUrl; di.obexServiceFound = true; devices.put(key, di); } private void updateDevices(String selected) { cbDevices.removeAllItems(); if (devices.size() == 0) { cbDevices.addItem("{no device found}"); btSend.setEnabled(false); cbDevices.setEnabled(false); } else { for (Enumeration i = devices.keys(); i.hasMoreElements();) { String addr = (String) i.nextElement(); DeviceInfo di = (DeviceInfo) devices.get(addr); cbDevices.addItem(di); if ((selected != null) && (selected.equals(di.btAddress))) { cbDevices.setSelectedItem(di); } } cbDevices.setEnabled(true); btSend.setEnabled(true); } } private void bluetoothDiscovery() { final Timer timer = new Timer(1000, new DiscoveryTimerListener()); progressBar.setMaximum(BLUETOOTH_DISCOVERY_STD_SEC); setProgressValue(0); Thread t = new Thread() { public void run() { if (bluetoothInquirer.startInquiry()) { iconLabel.setIcon(searchIcon); setStatus("Bluetooth discovery started"); btFindDevice.setEnabled(false); btSend.setEnabled(false); timer.start(); while (bluetoothInquirer.inquiring) { try { Thread.sleep(1000); } catch (Exception e) { } } timer.stop(); // setStatus("Bluetooth discovery finished"); setProgressValue(0); int idx = 0; progressBar.setMaximum(bluetoothInquirer.devices.size()); for (Iterator iter = bluetoothInquirer.devices.iterator(); iter.hasNext();) { RemoteDevice dev = (RemoteDevice) iter.next(); String obexUrl = bluetoothInquirer.findOBEX(dev.getBluetoothAddress()); if (obexUrl != null) { Logger.debug("found obex url", obexUrl); addDevice(dev.getBluetoothAddress(), BluetoothInquirer.getFriendlyName(dev), obexUrl); } idx++; setProgressValue(idx); } setProgressValue(0); saveConfig(); updateDevices(null); btFindDevice.setEnabled(true); btSend.setEnabled(true); iconLabel.setIcon(btIcon); } } }; t.start(); } private String blueSoleilFindOBEX(String btAddress, String obexUrl) { if ("bluesoleil".equals(LocalDevice.getProperty("bluecove.stack"))) { RemoteDevice dev = new RemoteDeviceExt(btAddress); String foundObexUrl = bluetoothInquirer.findOBEX(dev.getBluetoothAddress()); if (foundObexUrl != null) { Logger.debug("found", btAddress); addDevice(dev.getBluetoothAddress(), BluetoothInquirer.getFriendlyName(dev), foundObexUrl); } return foundObexUrl; } return obexUrl; } private DeviceInfo getSelectedDevice() { Object o = cbDevices.getSelectedItem(); if ((o == null) || !(o instanceof DeviceInfo)) { return null; } return (DeviceInfo) o; } private String getSelectedDeviceAddress() { DeviceInfo d = getSelectedDevice(); if (d == null) { return null; } return d.btAddress; } private void obexSend() { if (fileName == null) { setStatus("No file selected"); return; } final DeviceInfo d = getSelectedDevice(); if (d == null) { setStatus("No Device selected"); return; } final ObexBluetoothClient o = new ObexBluetoothClient(this, fileName, data); Thread t = new Thread() { public void run() { btSend.setEnabled(false); iconLabel.setIcon(transferIcon); String obexUrl = d.obexUrl; if (!d.obexServiceFound) { obexUrl = blueSoleilFindOBEX(d.btAddress, obexUrl); } if (obexUrl != null) { if (o.obexPut(obexUrl)) { selectNextFile(); } } else { setStatus("Service not found"); } btSend.setEnabled(true); iconLabel.setIcon(btIcon); saveConfig(); } }; t.start(); } private static String simpleFileName(String filePath) { int idx = filePath.lastIndexOf('/'); if (idx == -1) { idx = filePath.lastIndexOf('\\'); } if (idx == -1) { return filePath; } return filePath.substring(idx + 1); } void downloadFile(final String filePath) { Thread t = new Thread() { public void run() { InputStream is = null; try { iconLabel.setIcon(downloadIcon); String path = filePath; String inputFileName; File file = new File(filePath); if (file.exists()) { is = new FileInputStream(file); inputFileName = file.getName(); } else { URL url = new URL(path); is = url.openConnection().getInputStream(); inputFileName = url.getFile(); } ByteArrayOutputStream bos = new ByteArrayOutputStream(); byte[] buffer = new byte[0xFF]; int i = is.read(buffer); int done = 0; while (i != -1) { bos.write(buffer, 0, i); done += i; // setProgressValue(done); i = is.read(buffer); } data = bos.toByteArray(); fileName = simpleFileName(inputFileName); setStatus((data.length / 1024) + "k " + fileName); } catch (Throwable e) { Logger.error(e); setStatus("Download error " + e.getMessage()); } finally { IOUtils.closeQuietly(is); iconLabel.setIcon(btIcon); } } }; t.start(); } private void shutdown() { if (bluetoothInquirer != null) { bluetoothInquirer.shutdown(); bluetoothInquirer = null; } } }