/**
   * Start desktop capture stream.
   *
   * @see AbstractPullBufferStream#start()
   */
  @Override
  public void start() throws IOException {
    super.start();

    if (desktopInteract == null) {
      try {
        desktopInteract = new DesktopInteractImpl();
      } catch (Exception e) {
        logger.warn("Cannot create DesktopInteract object!");
      }
    }
  }
  /**
   * Creates an account for the given user and password.
   *
   * @param providerFactory the ProtocolProviderFactory which will create the account
   * @param user the user identifier
   * @return the <tt>ProtocolProviderService</tt> for the new account.
   */
  public ProtocolProviderService installAccount(
      ProtocolProviderFactory providerFactory, String user) throws OperationFailedException {
    Hashtable<String, String> accountProperties = new Hashtable<String, String>();

    accountProperties.put(
        ProtocolProviderFactory.ACCOUNT_ICON_PATH,
        "resources/images/protocol/gibberish/gibberish32x32.png");

    if (registration.isRememberPassword()) {
      accountProperties.put(ProtocolProviderFactory.PASSWORD, registration.getPassword());
    }

    if (isModification()) {
      providerFactory.uninstallAccount(protocolProvider.getAccountID());
      this.protocolProvider = null;
      setModification(false);
    }

    try {
      AccountID accountID = providerFactory.installAccount(user, accountProperties);

      ServiceReference serRef = providerFactory.getProviderForAccount(accountID);

      protocolProvider =
          (ProtocolProviderService) GibberishAccRegWizzActivator.bundleContext.getService(serRef);
    } catch (IllegalStateException exc) {
      logger.warn(exc.getMessage());

      throw new OperationFailedException(
          "Account already exists.", OperationFailedException.IDENTIFICATION_CONFLICT);
    } catch (Exception exc) {
      logger.warn(exc.getMessage());

      throw new OperationFailedException(
          "Failed to add account", OperationFailedException.GENERAL_ERROR);
    }

    return protocolProvider;
  }
  /**
   * Creates an account for the given Account ID, Identity File and Known Hosts File
   *
   * @param providerFactory the ProtocolProviderFactory which will create the account
   * @param user the user identifier
   * @return the <tt>ProtocolProviderService</tt> for the new account.
   */
  public ProtocolProviderService installAccount(
      ProtocolProviderFactory providerFactory, String user) throws OperationFailedException {
    Hashtable<String, String> accountProperties = new Hashtable<String, String>();

    accountProperties.put(
        ProtocolProviderFactory.ACCOUNT_ICON_PATH, "resources/images/protocol/ssh/ssh32x32.png");

    accountProperties.put(
        ProtocolProviderFactory.NO_PASSWORD_REQUIRED, new Boolean(true).toString());

    accountProperties.put(
        ProtocolProviderFactorySSHImpl.IDENTITY_FILE, registration.getIdentityFile());

    accountProperties.put(
        ProtocolProviderFactorySSHImpl.KNOWN_HOSTS_FILE,
        String.valueOf(registration.getKnownHostsFile()));

    try {
      AccountID accountID = providerFactory.installAccount(user, accountProperties);

      ServiceReference serRef = providerFactory.getProviderForAccount(accountID);

      protocolProvider =
          (ProtocolProviderService) SSHAccRegWizzActivator.bundleContext.getService(serRef);
    } catch (IllegalStateException exc) {
      logger.warn(exc.getMessage());

      throw new OperationFailedException(
          "Account already exists.", OperationFailedException.IDENTIFICATION_CONFLICT);
    } catch (Exception exc) {
      logger.warn(exc.getMessage());

      throw new OperationFailedException(
          "Failed to add account", OperationFailedException.GENERAL_ERROR);
    }

    return protocolProvider;
  }
  /**
   * Updates this view i.e. <tt>OneToOneCallPeerPanel</tt> so that it depicts the current state of
   * its model i.e. <tt>callPeer</tt>. The update is performed in the AWT event dispatching thread.
   */
  private void updateViewFromModelInEventDispatchThread() {
    /*
     * We receive events/notifications from various threads and we respond
     * to them in the AWT event dispatching thread. It is possible to first
     * schedule an event to be brought to the AWT event dispatching thread,
     * then to have #dispose() invoked on this instance and, finally, to
     * receive the scheduled event in the AWT event dispatching thread. In
     * such a case, this disposed instance should not respond to the event
     * because it may, for example, steal a visual Components depicting
     * video (which cannot belong to more than one parent at a time) from
     * another non-disposed OneToOneCallPeerPanel.
     */
    if (disposed) return;

    /*
     * Update the display of visual <tt>Component</tt>s depicting video
     * streaming between the local peer/user and the remote peer(s).
     */

    OperationSetVideoTelephony videoTelephony =
        callPeer.getProtocolProvider().getOperationSet(OperationSetVideoTelephony.class);
    Component remoteVideo = null;
    Component localVideo = null;

    if (videoTelephony != null) {
      List<Component> remoteVideos = videoTelephony.getVisualComponents(callPeer);

      if ((remoteVideos != null) && !remoteVideos.isEmpty()) {
        /*
         * TODO OneToOneCallPeerPanel displays a one-to-one conversation
         * between the local peer/user and a specific remote peer. If
         * the remote peer is the focus of a telephony conference of its
         * own, it may be sending multiple videos to the local peer.
         * Switching to a user interface which displays multiple videos
         * is the responsibility of whoever decided that this
         * OneToOneCallPeerPanel is to be used to depict the current
         * state of the CallConference associated with the CallPeer
         * depicted by this instance. If that switching decides that
         * this instance is to continue being the user interface, then
         * we should probably pick up the remote video which is
         * generated by the remote peer and not one of its
         * ConferenceMembers.
         */
        remoteVideo = remoteVideos.get(0);
      }

      if (uiVideoHandler.isLocalVideoVisible()) {
        try {
          localVideo = videoTelephony.getLocalVisualComponent(callPeer);
        } catch (OperationFailedException ofe) {
          /*
           * Well, we cannot do much about the exception. We'll just
           * not display the local video.
           */
          logger.warn("Failed to retrieve local video to be displayed.", ofe);
        }
      }

      /*
       * Determine whether there is actually a change in the local and
       * remote videos which requires an update.
       */
      boolean localVideoChanged =
          ((localVideo != this.localVideo)
              || ((localVideo != null) && !UIVideoHandler2.isAncestor(center, localVideo)));
      boolean remoteVideoChanged =
          ((remoteVideo != this.remoteVideo)
              || ((remoteVideo != null) && !UIVideoHandler2.isAncestor(center, remoteVideo)));

      // If the remote video has changed, maybe the CallPanel can display
      // the LO/SD/HD button.
      if (remoteVideoChanged) {
        // Updates video component which may listen the mouse and key
        // events.
        if (desktopSharingMouseAndKeyboardListener != null) {
          desktopSharingMouseAndKeyboardListener.setVideoComponent(remoteVideo);
        }

        CallPanel callPanel = callRenderer.getCallContainer();
        // The remote video has been added, then tries to display the
        // LO/SD/HD button.
        if (remoteVideo != null) {
          callPanel.addRemoteVideoSpecificComponents(callPeer);
        }
        // The remote video has been removed, then hide the LO/SD/HD
        // button if it is currently displayed.
        else {
          callPanel.removeRemoteVideoSpecificComponents();
        }
      }

      if (localVideoChanged || remoteVideoChanged) {
        /*
         * VideoContainer and JAWTRenderer cannot handle random
         * additions of Components. Removing the localVideo when the
         * user has requests its hiding though, should work without
         * removing all Components from the VideoCotainer and adding
         * them again.
         */
        if (localVideoChanged && !remoteVideoChanged && (localVideo == null)) {
          if (this.localVideo != null) {
            center.remove(this.localVideo);
            this.localVideo = null;

            if (closeLocalVisualComponentButton != null)
              center.remove(closeLocalVisualComponentButton);
          }
        } else {
          center.removeAll();
          this.localVideo = null;
          this.remoteVideo = null;

          /*
           * AWT does not make a guarantee about the Z order even
           * within an operating system i.e. the order of adding the
           * Components to their Container does not mean that they
           * will be determinedly painted in that or reverse order.
           * Anyway, there appears to be an expectation among the
           * developers less acquainted with AWT that AWT paints the
           * Components of a Container in an order that is the reverse
           * of the order of their adding. In order to satisfy that
           * expectation and thus give at least some idea to the
           * developers reading the code bellow, do add the Components
           * according to that expectation.
           */

          if (localVideo != null) {
            if (closeLocalVisualComponentButton == null) {
              closeLocalVisualComponentButton = new CloseLocalVisualComponentButton(uiVideoHandler);
            }
            center.add(closeLocalVisualComponentButton, VideoLayout.CLOSE_LOCAL_BUTTON, -1);

            center.add(localVideo, VideoLayout.LOCAL, -1);
            this.localVideo = localVideo;
          }

          if (remoteVideo != null) {
            center.add(remoteVideo, VideoLayout.CENTER_REMOTE, -1);
            this.remoteVideo = remoteVideo;
          }
        }
      }
    }
  }