package tim.prune.function.gpsies; import java.awt.BorderLayout; import java.awt.Component; import java.awt.FlowLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import java.io.BufferedInputStream; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.io.PipedInputStream; import java.io.PipedOutputStream; import java.net.MalformedURLException; import java.net.URL; import javax.swing.BorderFactory; import javax.swing.BoxLayout; import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JDialog; import javax.swing.JLabel; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JPasswordField; import javax.swing.JScrollPane; import javax.swing.JTextArea; import javax.swing.JTextField; import javax.swing.border.EtchedBorder; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import tim.prune.App; import tim.prune.GenericFunction; import tim.prune.I18nManager; import tim.prune.function.browser.BrowserLauncher; import tim.prune.gui.GuiGridLayout; import tim.prune.save.GpxExporter; import tim.prune.save.SettingsForExport; /** * Function to upload track information up to Gpsies.com */ public class UploadGpsiesFunction extends GenericFunction { /** Dialog object */ private JDialog _dialog = null; /** Edit box for user name */ private JTextField _usernameField = null; /** Edit box for password */ private JPasswordField _passwordField = null; /** Name of track */ private JTextField _nameField = null; /** Description */ private JTextArea _descField = null; /** Private checkbox */ private JCheckBox _privateCheckbox = null; /** Activity checkboxes */ private JCheckBox[] _activityCheckboxes = null; /** Writer object for GPX export */ private OutputStreamWriter _writer = null; /** upload button */ private JButton _uploadButton = null; /** URL to post form to */ private static final String GPSIES_URL = "http://www.gpsies.com/upload.do"; /** Keys for describing activities */ private static final String[] ACTIVITY_KEYS = {"trekking", "walking", "jogging", "biking", "motorbiking", "snowshoe", "sailing", "skating"}; /** * Constructor * @param inApp App object */ public UploadGpsiesFunction(App inApp) { super(inApp); } /** * @return name key */ public String getNameKey() { return "function.uploadgpsies"; } /** * Begin the function */ public void begin() { // Initialise dialog, show empty list if (_dialog == null) { _dialog = new JDialog(_parentFrame, I18nManager.getText(getNameKey()), true); _dialog.setLocationRelativeTo(_parentFrame); _dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE); _dialog.getContentPane().add(makeDialogComponents()); _dialog.pack(); } // Show dialog _dialog.setVisible(true); } /** * Create dialog components * @return Panel containing all gui elements in dialog */ private Component makeDialogComponents() { JPanel dialogPanel = new JPanel(); dialogPanel.setLayout(new BorderLayout()); JPanel gridPanel = new JPanel(); GuiGridLayout grid = new GuiGridLayout(gridPanel); grid.add(new JLabel(I18nManager.getText("dialog.gpsies.username"))); _usernameField = new JTextField(15); grid.add(_usernameField); grid.add(new JLabel(I18nManager.getText("dialog.gpsies.password"))); _passwordField = new JPasswordField(15); grid.add(_passwordField); // Track name and description grid.add(new JLabel(I18nManager.getText("dialog.gpsies.column.name"))); _nameField = new JTextField(15); grid.add(_nameField); grid.add(new JLabel(I18nManager.getText("dialog.gpsies.description"))); _descField = new JTextArea(5, 15); _descField.setLineWrap(true); _descField.setWrapStyleWord(true); grid.add(new JScrollPane(_descField)); // Listener on all these text fields to enable/disable the ok button KeyAdapter keyListener = new KeyAdapter() { /** Key released */ public void keyReleased(KeyEvent inE) { enableOK(); if (inE.getKeyCode() == KeyEvent.VK_ESCAPE) { _dialog.dispose(); } } }; _usernameField.addKeyListener(keyListener); _passwordField.addKeyListener(keyListener); _nameField.addKeyListener(keyListener); // Listen for tabs on description field, to change focus not enter tabs _descField.addKeyListener(new KeyAdapter() { /** Key pressed */ public void keyPressed(KeyEvent inE) { if (inE.getKeyCode() == KeyEvent.VK_TAB) { inE.consume(); if (inE.isShiftDown()) { _nameField.requestFocusInWindow(); } else { _privateCheckbox.requestFocusInWindow(); } } } }); // Listen for Ctrl-backspace on password field (delete contents) _passwordField.addKeyListener(new KeyAdapter() { /** Key released */ public void keyReleased(KeyEvent inE) { if (inE.isControlDown() && (inE.getKeyCode() == KeyEvent.VK_BACK_SPACE || inE.getKeyCode() == KeyEvent.VK_DELETE)) { _passwordField.setText(""); } } }); // Checkbox for private / public grid.add(new JLabel(I18nManager.getText("dialog.gpsies.keepprivate"))); _privateCheckbox = new JCheckBox(); _privateCheckbox.setSelected(true); grid.add(_privateCheckbox); // panel for activity type checkboxes JPanel activityPanel = new JPanel(); activityPanel.setBorder(BorderFactory.createEtchedBorder(EtchedBorder.LOWERED)); ChangeListener checkListener = new ChangeListener() { public void stateChanged(ChangeEvent arg0) { enableOK(); } }; // Why not a simple grid layout here? GuiGridLayout actGrid = new GuiGridLayout(activityPanel, new double[] {1.0, 1.0}, new boolean[] {false, false}); final int numActivities = ACTIVITY_KEYS.length; _activityCheckboxes = new JCheckBox[numActivities]; for (int i=0; i<numActivities; i++) { _activityCheckboxes[i] = new JCheckBox(I18nManager.getText("dialog.gpsies.activity." + ACTIVITY_KEYS[i])); _activityCheckboxes[i].addChangeListener(checkListener); actGrid.add(_activityCheckboxes[i]); } grid.add(new JLabel(I18nManager.getText("dialog.gpsies.activities"))); grid.add(activityPanel); JPanel midPanel = new JPanel(); midPanel.setLayout(new BoxLayout(midPanel, BoxLayout.Y_AXIS)); midPanel.add(gridPanel); dialogPanel.add(midPanel, BorderLayout.CENTER); // button panel at bottom JPanel buttonPanel = new JPanel(); buttonPanel.setLayout(new FlowLayout(FlowLayout.RIGHT)); _uploadButton = new JButton(I18nManager.getText("button.upload")); _uploadButton.setEnabled(false); _uploadButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { startUpload(); } }); buttonPanel.add(_uploadButton); JButton cancelButton = new JButton(I18nManager.getText("button.cancel")); cancelButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { _dialog.dispose(); } }); buttonPanel.add(cancelButton); dialogPanel.add(buttonPanel, BorderLayout.SOUTH); dialogPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 15)); return dialogPanel; } /** * Check the inputs and enable or disable the upload button */ private void enableOK() { // Check for lengths of input fields - only username, password and filename are required boolean ok = (_usernameField.getText().length() > 0 && _nameField.getText().length() > 0); if (ok) { // also check password field char[] pass = _passwordField.getPassword(); ok = pass.length > 0; for (int i=0; i<pass.length; i++) {pass[i] = '0';} // recommended by javadoc if (ok) { ok = false; for (int i=0; i<_activityCheckboxes.length; i++) { ok = ok || _activityCheckboxes[i].isSelected(); } } } _uploadButton.setEnabled(ok); } /** * Start the upload process (require separate thread?) */ private void startUpload() { BufferedReader reader = null; try { FormPoster poster = new FormPoster(new URL(GPSIES_URL)); poster.setParameter("device", "Prune"); poster.setParameter("username", _usernameField.getText()); poster.setParameter("password", new String(_passwordField.getPassword())); boolean hasActivity = false; for (int i=0; i<ACTIVITY_KEYS.length; i++) { if (_activityCheckboxes[i].isSelected()) { hasActivity = true; poster.setParameter("trackTypes", ACTIVITY_KEYS[i]); } } if (!hasActivity) {poster.setParameter("trackTypes", "walking");} // default if none given poster.setParameter("filename", _nameField.getText()); poster.setParameter("fileDescription", _descField.getText()); poster.setParameter("startpointCountry", "DE"); poster.setParameter("endpointCountry", "DE"); // both those will be corrected by gpsies poster.setParameter("status", (_privateCheckbox.isSelected()?"3":"1")); poster.setParameter("submit", "speichern"); // required // Use Pipes to connect the GpxExporter's output with the FormPoster's input PipedInputStream iStream = new PipedInputStream(); PipedOutputStream oStream = new PipedOutputStream(iStream); _writer = new OutputStreamWriter(oStream); new Thread(new Runnable() { public void run() { try { GpxExporter.exportData(_writer, _app.getTrackInfo(), _nameField.getText(), null, new SettingsForExport(), null); } catch (IOException e) {} finally { try {_writer.close();} catch (IOException e) {} } } }).start(); poster.setParameter("formFile", "filename.gpx", iStream); BufferedInputStream answer = new BufferedInputStream(poster.post()); int response = poster.getResponseCode(); reader = new BufferedReader(new InputStreamReader(answer)); String line = reader.readLine(); // Try to extract gpsies page url from the returned message String pageUrl = null; if (response == 200 && line.substring(0, 2).toUpperCase().equals("OK")) { final int bracketPos = line.indexOf('['); if (bracketPos > 0 && line.endsWith("]")) { pageUrl = line.substring(bracketPos + 1, line.length()-1); } } if (pageUrl != null) { // OK received and managed to extract a Url from the return message. int userChoice = JOptionPane.showConfirmDialog(_app.getFrame(), I18nManager.getText("dialog.gpsies.confirmopenpage"), I18nManager.getText(getNameKey()), JOptionPane.OK_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE); if (userChoice == JOptionPane.OK_OPTION) { BrowserLauncher.launchBrowser(pageUrl); } } else { _app.showErrorMessageNoLookup(getNameKey(), I18nManager.getText("error.gpsies.uploadnotok") + ": " + line); } } catch (MalformedURLException e) {} catch (IOException ioe) { _app.showErrorMessageNoLookup(getNameKey(), I18nManager.getText("error.gpsies.uploadfailed") + ": " + ioe.getClass().getName() + " : " + ioe.getMessage()); } finally { try {if (reader != null) reader.close();} catch (IOException e) {} } _dialog.dispose(); } }