package com.mobilesorcery.sdk.ui.targetphone.internal.bt; import java.text.MessageFormat; import java.util.ArrayList; import java.util.Set; import java.util.TreeSet; import javax.bluetooth.BluetoothStateException; import javax.bluetooth.DeviceClass; import javax.bluetooth.DiscoveryAgent; import javax.bluetooth.DiscoveryListener; import javax.bluetooth.LocalDevice; import javax.bluetooth.RemoteDevice; import javax.bluetooth.ServiceRecord; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.jface.dialogs.Dialog; import org.eclipse.jface.dialogs.IDialogConstants; import org.eclipse.jface.layout.TableColumnLayout; import org.eclipse.jface.util.Policy; import org.eclipse.jface.viewers.ArrayContentProvider; import org.eclipse.jface.viewers.ColumnWeightData; import org.eclipse.jface.viewers.ISelectionChangedListener; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.viewers.SelectionChangedEvent; import org.eclipse.jface.viewers.TableViewer; import org.eclipse.swt.SWT; import org.eclipse.swt.events.MouseAdapter; import org.eclipse.swt.events.MouseEvent; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Button; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.ProgressBar; import org.eclipse.swt.widgets.Shell; import org.eclipse.swt.widgets.Table; import org.eclipse.swt.widgets.TableColumn; import com.mobilesorcery.sdk.ui.UIUtils; import com.mobilesorcery.sdk.ui.targetphone.TargetPhonePlugin; /** * Pops up a dialog that can be used to select a bluetooth device in a list of * discovered bluetooth devices. * * @author fmattias */ public class BluetoothDialog extends Dialog { private final static int REFRESH_ID = 0xff; private TableViewer deviceList; private boolean refreshInProgress = true; private ProgressBar scanProgress; private Label scanResults; private BluetoothDeviceDiscoverer discoverer; private BluetoothDevice selectedDevice; public BluetoothDialog(Shell parent) { super(parent); discoverer = new BluetoothDeviceDiscoverer(new UIUpdater(this)); } public boolean isResizable() { return true; } public void configureShell(Shell newShell) { super.configureShell(newShell); newShell.setText("Discover Bluetooth Devices"); } public int open() { discoverDevices(); return super.open(); } protected Control createDialogArea(Composite container) { Composite parent = (Composite) super.createDialogArea(container); Composite status = new Composite(parent, SWT.NONE); status.setLayout(new GridLayout(2, false)); status.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); scanResults = new Label(status, SWT.NONE); scanResults.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); scanProgress = new ProgressBar(status, SWT.INDETERMINATE); GridData scanProgressData = new GridData(UIUtils.getDefaultFieldSize() / 2, SWT.DEFAULT); scanProgressData.horizontalAlignment = SWT.RIGHT; scanProgress.setLayoutData(scanProgressData); Composite deviceComposite = new Composite(parent, SWT.NONE); deviceList = new TableViewer(deviceComposite, SWT.BORDER | SWT.SINGLE); TableColumnLayout deviceListTableLayout = new TableColumnLayout(); deviceComposite.setLayout(deviceListTableLayout); TableColumn column1 = new TableColumn(deviceList.getTable(), SWT.NONE); deviceListTableLayout.setColumnData(column1, new ColumnWeightData(70)); deviceList.setLabelProvider(new BluetoothLabelProvider(deviceList)); deviceList.setContentProvider(new ArrayContentProvider()); deviceList.addSelectionChangedListener(new ISelectionChangedListener() { public void selectionChanged(SelectionChangedEvent event) { selectedDevice = (BluetoothDevice) ((IStructuredSelection) event.getSelection()).getFirstElement(); updateUI(); } }); deviceList.getControl().addMouseListener(new MouseAdapter() { public void mouseDoubleClick(MouseEvent event) { // For some weird reason, adding a double click listener doesn't // work. if (selectedDevice != null) { okPressed(); } } }); GridData gd = new GridData(GridData.FILL_BOTH); gd.heightHint = convertHeightInCharsToPixels(15); gd.widthHint = convertWidthInCharsToPixels(80); deviceComposite.setLayoutData(gd); Table table = deviceList.getTable(); table.setFont(container.getFont()); updateUI(); return parent; } protected Control createButtonBar(Composite parent) { Composite composite = new Composite(parent, SWT.NONE); GridLayout layout = new GridLayout(); layout.marginWidth = convertHorizontalDLUsToPixels(7); layout.marginHeight = convertVerticalDLUsToPixels(7); layout.horizontalSpacing = convertHorizontalDLUsToPixels(4); layout.verticalSpacing = convertVerticalDLUsToPixels(4); composite.setLayout(layout); composite.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); Button refreshButton = createButton(composite, REFRESH_ID, "Refresh", false); setButtonLayout(refreshButton, SWT.LEFT); // ((GridData) refreshButton.getLayoutData()).horizontalIndent = // convertHorizontalDLUsToPixels(7); // ((GridData) refreshButton.getLayoutData()).verticalIndent = // convertHorizontalDLUsToPixels(4); Composite rightAligned = new Composite(composite, 0); GridLayout rightAlignedLayout = new GridLayout(); rightAlignedLayout.marginWidth = 0; rightAlignedLayout.marginHeight = 0; rightAlignedLayout.horizontalSpacing = 0; rightAlignedLayout.verticalSpacing = layout.verticalSpacing; rightAligned.setLayout(rightAlignedLayout); setButtonLayout(createButton(rightAligned, IDialogConstants.OK_ID, IDialogConstants.OK_LABEL, true), SWT.RIGHT); Button cancelButton = createButton(rightAligned, IDialogConstants.CANCEL_ID, IDialogConstants.CANCEL_LABEL, false); setButtonLayout(cancelButton, SWT.RIGHT); ((GridData) cancelButton.getLayoutData()).horizontalIndent = layout.horizontalSpacing; GridData rightAlignedData = new GridData(GridData.FILL_HORIZONTAL | GridData.GRAB_HORIZONTAL); rightAlignedData.horizontalAlignment = SWT.RIGHT; rightAligned.setLayoutData(rightAlignedData); setRefreshInProgress(refreshInProgress); return composite; } protected void okPressed() { discoverer.cancelDiscovery(); super.okPressed(); } protected void cancelPressed() { discoverer.cancelDiscovery(); super.cancelPressed(); } protected void createButtonsForButtonBar(Composite parent) { } protected void setButtonLayout(Button button, int vAlignment) { setButtonLayoutData(button); ((GridData) button.getLayoutData()).verticalAlignment = vAlignment; } protected void buttonPressed(int buttonId) { if (buttonId == REFRESH_ID) { discoverDevices(); } else { super.buttonPressed(buttonId); } } public void setRefreshInProgress(final boolean refreshInProgress) { this.refreshInProgress = refreshInProgress; if (getShell() == null) { return; } getShell().getDisplay().asyncExec(new Runnable() { public void run() { updateUI(); } }); } protected void updateUI() { Button refreshButton = getButton(REFRESH_ID); if (refreshButton != null) { deviceList.setInput(discoverer.m_devices.toArray()); deviceList.getControl().getParent().layout(); refreshButton.setEnabled(!refreshInProgress); scanProgress.setVisible(refreshInProgress); updateScanResults(); getButton(IDialogConstants.OK_ID).setEnabled(selectedDevice != null); } } protected void updateScanResults() { scanResults.setText(MessageFormat.format("Found {0} device(s)", discoverer.m_devices.size())); } /** * This function starts the discovery for devices. The function starts the * search in a new thread, and will return before all devices has been * found. */ public void discoverDevices() { refreshInProgress = true; updateUI(); discoverer.discoverDevices(); } /** * Handles the event of a device being discovered. * * @author fmattias */ public class BluetoothDeviceDiscoverer implements DiscoveryListener { /** * List of devices that has been discovered so far. */ private TreeSet<BluetoothDevice> m_devices; /** * Class called when a device is discovered. */ private DeviceUpdate m_updater; private DiscoveryAgent agent; /** * * @param updater * Class that should be called when a device is found. */ public BluetoothDeviceDiscoverer(DeviceUpdate updater) { clearDevices(); m_updater = updater; } void clearDevices() { m_devices = new TreeSet<BluetoothDevice>(BluetoothDevice.COMPARATOR); } public void discoverDevices() { clearDevices(); updateUI(); /* Get local dongle and set up an agent for device discovery */ LocalDevice dongle = null; try { dongle = LocalDevice.getLocalDevice(); agent = dongle.getDiscoveryAgent(); agent.startInquiry(DiscoveryAgent.GIAC, this); } catch (BluetoothStateException e) { setRefreshInProgress(false); Policy.getStatusHandler().show(new Status(IStatus.ERROR, TargetPhonePlugin.PLUGIN_ID, e.getMessage()), "Could not scan for devices"); } } public void cancelDiscovery() { if (agent != null) { new Thread(new Runnable() { public void run() { agent.cancelInquiry(BluetoothDeviceDiscoverer.this); } }, "Cancel BT discovery"); } } public void deviceDiscovered(RemoteDevice device, DeviceClass type) { BluetoothDevice btDevice = new BluetoothDevice(device, type); /* Only add devices that are not available */ if (!m_devices.contains(btDevice)) { m_devices.add(btDevice); m_updater.deviceFound(btDevice); } } public void inquiryCompleted(int discType) { Set<BluetoothDevice> devices = m_devices; for (BluetoothDevice device : devices) { device.resolveFriendlyName(m_updater); } setRefreshInProgress(false); } public void serviceSearchCompleted(int arg0, int arg1) { } public void servicesDiscovered(int arg0, ServiceRecord[] arg1) { } public ArrayList<BluetoothDevice> getDevices() { return new ArrayList<BluetoothDevice>(m_devices); } } /** * Interface used when a device is discovered. */ public interface DeviceUpdate { DeviceUpdate NULL = new DeviceUpdate() { public void deviceUpdated(BluetoothDevice device) { } public void deviceFound(BluetoothDevice device) { } }; /** * Is called once a device is discovered. * * @param device * The devices that has been discovered. */ public void deviceFound(BluetoothDevice device); public void deviceUpdated(BluetoothDevice device); } /** * Updates the UI as soon as a device is discovered. */ public class UIUpdater implements DeviceUpdate, Runnable { /** * Main widget that will be updated. */ BluetoothDialog m_parent; public UIUpdater(BluetoothDialog parent) { m_parent = parent; } public void deviceFound(final BluetoothDevice device) { discoverer.m_devices.add(device); deviceUpdated(device); } public void deviceUpdated(BluetoothDevice device) { if (m_parent.getShell() != null) { m_parent.getShell().getDisplay().syncExec(this); } } public void run() { if (deviceList != null) { updateUI(); } } } public BluetoothDevice getSelectedDevice() { return selectedDevice; } }