From 2a2cfac04817bc6d28f679f4e874256ee1379e39 Mon Sep 17 00:00:00 2001 From: Damian Minkov Date: Thu, 23 Dec 2010 15:16:37 +0000 Subject: [PATCH] Change account wizards to register protocol in separate thread and no more block the UI and shows status text. --- resources/languages/resources.properties | 2 +- .../gui/customcontrols/wizard/Wizard.java | 57 ++++- .../wizard/WizardController.java | 91 ++++--- .../gui/main/account/NewAccountDialog.java | 223 ++++++++++++++---- 4 files changed, 296 insertions(+), 77 deletions(-) diff --git a/resources/languages/resources.properties b/resources/languages/resources.properties index f325f94ad..093db0005 100644 --- a/resources/languages/resources.properties +++ b/resources/languages/resources.properties @@ -715,7 +715,7 @@ plugin.jabberaccregwizz.RELAY_SUPPORT=Support relay plugin.jabberaccregwizz.ADD_JINGLE_NODE=Add Jingle Node plugin.jabberaccregwizz.JID_ADDRESS=JID Address plugin.jabberaccregwizz.ADDITIONAL_JINGLE_NODES=Additional Jingle Nodes -plugin.jabberaccregwizz.USE_UPNP=Use UPnP +plugin.jabberaccregwizz.USE_UPNP=Use UPnP (Experimental) # mailbox plugin.mailbox.OUTGOING=Outgoing Message: diff --git a/src/net/java/sip/communicator/impl/gui/customcontrols/wizard/Wizard.java b/src/net/java/sip/communicator/impl/gui/customcontrols/wizard/Wizard.java index c0d5d7f34..bb2fdd6fc 100644 --- a/src/net/java/sip/communicator/impl/gui/customcontrols/wizard/Wizard.java +++ b/src/net/java/sip/communicator/impl/gui/customcontrols/wizard/Wizard.java @@ -107,6 +107,16 @@ public class Wizard private final java.util.List wizardListeners = new Vector(); + /** + * Status label, show when connecting. + */ + private JLabel statusLabel = new JLabel(); + + /** + * If account is signing-in ignore close. + */ + private boolean isCurrentlySigningIn = false; + /** * This method accepts a java.awt.Dialog object as the javax.swing.JDialog's * parent. @@ -327,7 +337,8 @@ public boolean isBackButtonEnabled() */ public void setBackButtonEnabled(boolean newValue) { - wizardModel.setBackButtonEnabled(newValue); + if(!(isCurrentlySigningIn && newValue)) + wizardModel.setBackButtonEnabled(newValue); } /** @@ -460,6 +471,12 @@ private void initComponents() buttonPanel.add(buttonBox, java.awt.BorderLayout.EAST); + JPanel statusPanel = new TransparentPanel( + new FlowLayout(FlowLayout.CENTER)); + statusPanel.setBorder(new EmptyBorder(new Insets(5, 10, 5, 10))); + statusPanel.add(statusLabel); + buttonPanel.add(statusPanel, java.awt.BorderLayout.CENTER); + java.awt.Container contentPane = getContentPane(); contentPane.add(buttonPanel, java.awt.BorderLayout.SOUTH); contentPane.add(cardPanel, java.awt.BorderLayout.CENTER); @@ -531,6 +548,9 @@ private void fireWizardEvent(int eventCode) */ protected void close(boolean isEscaped) { + if(isCurrentlySigningIn) + return; + this.close(Wizard.CANCEL_RETURN_CODE); } @@ -664,4 +684,39 @@ public void setFinishButtonText(String text) { this.setFinishButtonDefaultText(text); } + + /** + * Changes cursor and status label, informing user we are in process + * of connecting. + */ + void startCommittingPage() + { + isCurrentlySigningIn = true; + + setBackButtonEnabled(false); + setCancelButtonEnabled(false); + setNextFinishButtonEnabled(false); + + statusLabel.setText(GuiActivator.getResources().getI18NString( + "service.gui.CONNECTING")); + + setCursor(new Cursor(Cursor.WAIT_CURSOR)); + } + + /** + * Changes cursor and status label, informing user we finished the process + * of connecting. + */ + void stopCommittingPage() + { + isCurrentlySigningIn = false; + + statusLabel.setText(""); + + setCursor(new Cursor(Cursor.DEFAULT_CURSOR)); + + setBackButtonEnabled(true); + setCancelButtonEnabled(true); + setNextFinishButtonEnabled(true); + } } diff --git a/src/net/java/sip/communicator/impl/gui/customcontrols/wizard/WizardController.java b/src/net/java/sip/communicator/impl/gui/customcontrols/wizard/WizardController.java index 3d7985243..11f1a981d 100644 --- a/src/net/java/sip/communicator/impl/gui/customcontrols/wizard/WizardController.java +++ b/src/net/java/sip/communicator/impl/gui/customcontrols/wizard/WizardController.java @@ -13,6 +13,8 @@ import net.java.sip.communicator.impl.gui.customcontrols.*; import net.java.sip.communicator.service.gui.*; +import javax.swing.*; + /** * This class is responsible for reacting to events generated by pushing any * of the three buttons, 'Next', 'Previous', and 'Cancel.' Based on what @@ -69,38 +71,11 @@ private void cancelButtonPressed() { * ID that the current panel identifies as the next panel, and displays * the panel that it's identifying. */ - private void nextButtonPressed() { - - WizardModel model = wizard.getModel(); - WizardPage page = model.getCurrentWizardPage(); - - try - { - page.commitPage(); - } - catch (Exception ex) - { - //lots of things may fail on page next, like for example parameter - //validation or account initialization. If this is what happened here - //just show an error and leave everything on the same page so that - //the user would have the chance to correct errors. - new ErrorDialog( - null, - GuiActivator.getResources().getI18NString("service.gui.ERROR"), - ex.getMessage(), - ex).showDialog(); - return; - } - - Object nextPageIdentifier = page.getNextPageIdentifier(); + private void nextButtonPressed() + { + wizard.startCommittingPage(); - if (nextPageIdentifier - .equals(WizardPage.FINISH_PAGE_IDENTIFIER)) { - wizard.close(Wizard.FINISH_RETURN_CODE); - } - else { - wizard.setCurrentPage(nextPageIdentifier); - } + new Thread(new PageCommitThread()).start(); } /** @@ -150,4 +125,58 @@ void resetButtonsToPanelRules() model.setNextFinishButtonText(wizard.getNextButtonDefaultText()); } } + + /** + * Runs committing page in new thread to avoid blocking UI thread. + */ + private class PageCommitThread + implements Runnable + { + /** + * Commits wizard page. + */ + public void run() + { + WizardModel model = wizard.getModel(); + final WizardPage page = model.getCurrentWizardPage(); + + try + { + page.commitPage(); + } + catch (Exception ex) + { + //lots of things may fail on page next, like for example parameter + //validation or account initialization. If this is what happened here + //just show an error and leave everything on the same page so that + //the user would have the chance to correct errors. + new ErrorDialog( + null, + GuiActivator.getResources().getI18NString("service.gui.ERROR"), + ex.getMessage(), + ex).showDialog(); + return; + } + + wizard.stopCommittingPage(); + + SwingUtilities.invokeLater(new Runnable() + { + public void run() + { + Object nextPageIdentifier = page.getNextPageIdentifier(); + + if (nextPageIdentifier + .equals(WizardPage.FINISH_PAGE_IDENTIFIER)) + { + wizard.close(Wizard.FINISH_RETURN_CODE); + } + else + { + wizard.setCurrentPage(nextPageIdentifier); + } + } + }); + } + } } diff --git a/src/net/java/sip/communicator/impl/gui/main/account/NewAccountDialog.java b/src/net/java/sip/communicator/impl/gui/main/account/NewAccountDialog.java index 5c7a03f7b..85c7e8d3a 100644 --- a/src/net/java/sip/communicator/impl/gui/main/account/NewAccountDialog.java +++ b/src/net/java/sip/communicator/impl/gui/main/account/NewAccountDialog.java @@ -73,6 +73,16 @@ public class NewAccountDialog private JEditorPane errorMessagePane; + /** + * If account is signing-in ignore close. + */ + private boolean isCurrentlySigningIn = false; + + /** + * Status label, show when connecting. + */ + private JLabel statusLabel = new JLabel(); + /** * Creates the dialog and initializes the UI. */ @@ -91,9 +101,14 @@ public NewAccountDialog() this.networkPanel.setBorder( BorderFactory.createEmptyBorder(5, 5, 5, 5)); + JPanel statusPanel = new TransparentPanel( + new FlowLayout(FlowLayout.CENTER)); + statusPanel.add(statusLabel); + this.mainPanel.add(buttonPanel, BorderLayout.SOUTH); this.buttonPanel.add(advancedButton, BorderLayout.WEST); this.buttonPanel.add(rightButtonPanel, BorderLayout.EAST); + this.buttonPanel.add(statusPanel, BorderLayout.CENTER); this.advancedButton.addActionListener(this); this.rightButtonPanel.add(addAccountButton); @@ -325,7 +340,7 @@ private void loadSelectedWizard(AccountRegistrationWizard wizard) addAccountButton.setEnabled( !(wizard instanceof EmptyAccountRegistrationWizard)); advancedButton.setEnabled( - !(wizard instanceof EmptyAccountRegistrationWizard)); + !(wizard instanceof EmptyAccountRegistrationWizard)); accountPanel.revalidate(); accountPanel.repaint(); @@ -397,6 +412,135 @@ public void actionPerformed(ActionEvent event) this.dispose(); } else if (sourceButton.equals(addAccountButton)) + { + startConnecting(wizardContainer); + + new Thread(new ProtocolSignInThread( + wizard, wizardContainer)).start(); + } + else if (sourceButton.equals(cancelButton)) + { + this.dispose(); + } + } + + /** + * Shows the new account dialog. + */ + public static void showNewAccountDialog() + { + if (newAccountDialog == null) + newAccountDialog = new NewAccountDialog(); + + newAccountDialog.pack(); + newAccountDialog.setVisible(true); + } + + /** + * Remove the newAccountDialog, when the window is closed. + * @param isEscaped indicates if the dialog has been escaped + */ + protected void close(boolean isEscaped) + { + if(isCurrentlySigningIn) + return; + + dispose(); + } + + /** + * Remove the newAccountDialog on dispose. + */ + public void dispose() + { + if(isCurrentlySigningIn) + return; + + newAccountDialog = null; + + super.dispose(); + } + + /** + * Overrides set visible to disable closing dialog if currently signing-in. + * + * @param visible + */ + @Override + public void setVisible(boolean visible) + { + if(isCurrentlySigningIn) + return; + + super.setVisible(visible); + } + + private void startConnecting(AccountRegWizardContainerImpl wizardContainer) + { + isCurrentlySigningIn = true; + + advancedButton.setEnabled(false); + addAccountButton.setEnabled(false); + cancelButton.setEnabled(false); + + statusLabel.setText(GuiActivator.getResources().getI18NString( + "service.gui.CONNECTING")); + + setCursor(new Cursor(Cursor.WAIT_CURSOR)); + } + + private void stopConnecting(AccountRegWizardContainerImpl wizardContainer) + { + isCurrentlySigningIn = false; + + statusLabel.setText(""); + + setCursor(new Cursor(Cursor.DEFAULT_CURSOR)); + + advancedButton.setEnabled(true); + addAccountButton.setEnabled(true); + cancelButton.setEnabled(true); + } + + /** + * Makes protocol operations in different thread. + */ + private class ProtocolSignInThread + implements Runnable + { + /** + * The wizard to use. + */ + AccountRegistrationWizard wizard; + + /** + * The container of the wizard. + */ + AccountRegWizardContainerImpl wizardContainer; + + /** + * Creates ProtocolSignInThread. + * @param wizard the wizard to use. + * @param wizardContainer the container of the wizard. + */ + ProtocolSignInThread(AccountRegistrationWizard wizard, + AccountRegWizardContainerImpl wizardContainer) + { + this.wizard = wizard; + this.wizardContainer = wizardContainer; + } + /** + * When an object implementing interface Runnable is used + * to create a thread, starting the thread causes the object's + * run method to be called in that separately executing + * thread. + *

+ * The general contract of the method run is that it may + * take any action whatsoever. + * + * @see Thread#run() + */ + public void run() { ProtocolProviderService protocolProvider; try @@ -404,7 +548,7 @@ else if (sourceButton.equals(addAccountButton)) if(wizard == emptyWizard) { loadErrorMessage(GuiActivator.getResources().getI18NString( - "service.gui.CHOOSE_NETWORK")); + "service.gui.CHOOSE_NETWORK")); } protocolProvider = wizard.signin(); @@ -413,11 +557,28 @@ else if (sourceButton.equals(addAccountButton)) { wizardContainer.saveAccountWizard(protocolProvider, wizard); - this.dispose(); + SwingUtilities.invokeLater(new Runnable() + { + public void run() + { + stopConnecting(wizardContainer); + + NewAccountDialog.this.dispose(); + } + }); } } catch (OperationFailedException e) { + // make sure buttons don't stay disabled + SwingUtilities.invokeLater(new Runnable() + { + public void run() + { + stopConnecting(wizardContainer); + } + }); + // If the sign in operation has failed we don't want to close // the dialog in order to give the user the possibility to // retry. @@ -428,23 +589,32 @@ else if (sourceButton.equals(addAccountButton)) == OperationFailedException.ILLEGAL_ARGUMENT) { loadErrorMessage(GuiActivator.getResources().getI18NString( - "service.gui.USERNAME_NULL")); + "service.gui.USERNAME_NULL")); } else if (e.getErrorCode() == OperationFailedException.IDENTIFICATION_CONFLICT) { loadErrorMessage(GuiActivator.getResources().getI18NString( - "service.gui.USER_EXISTS_ERROR")); + "service.gui.USER_EXISTS_ERROR")); } else { loadErrorMessage(GuiActivator.getResources().getI18NString( - "service.gui.ACCOUNT_CREATION_FAILED", - new String[]{e.getMessage()})); + "service.gui.ACCOUNT_CREATION_FAILED", + new String[]{e.getMessage()})); } } catch (Exception e) { + // make sure buttons don't stay disabled + SwingUtilities.invokeLater(new Runnable() + { + public void run() + { + stopConnecting(wizardContainer); + } + }); + // If the sign in operation has failed we don't want to close // the dialog in order to give the user the possibility to // retry. @@ -452,44 +622,9 @@ else if (e.getErrorCode() logger.debug("The sign in operation has failed."); loadErrorMessage(GuiActivator.getResources().getI18NString( - "service.gui.ACCOUNT_CREATION_FAILED", - new String[]{e.getMessage()})); + "service.gui.ACCOUNT_CREATION_FAILED", + new String[]{e.getMessage()})); } } - else if (sourceButton.equals(cancelButton)) - { - this.dispose(); - } - } - - /** - * Shows the new account dialog. - */ - public static void showNewAccountDialog() - { - if (newAccountDialog == null) - newAccountDialog = new NewAccountDialog(); - - newAccountDialog.pack(); - newAccountDialog.setVisible(true); - } - - /** - * Remove the newAccountDialog, when the window is closed. - * @param isEscaped indicates if the dialog has been escaped - */ - protected void close(boolean isEscaped) - { - dispose(); - } - - /** - * Remove the newAccountDialog on dispose. - */ - public void dispose() - { - newAccountDialog = null; - - super.dispose(); } }