/**
   * Returns the <tt>URL</tt> of the image corresponding to the given key.
   *
   * @param urlKey The identifier of the image in the resource properties file.
   * @return the <tt>URL</tt> of the image corresponding to the given key
   */
  public URL getImageURL(String urlKey) {
    String path = getImagePath(urlKey);

    if (path == null || path.length() == 0) {
      if (logger.isInfoEnabled()) logger.info("Missing resource for key: " + urlKey);
      return null;
    }
    return getImageURLForPath(path);
  }
  /**
   * Start the swing notification service
   *
   * @param bc
   * @throws java.lang.Exception
   */
  public void start(BundleContext bc) throws Exception {
    if (logger.isInfoEnabled()) logger.info("Swing Notification ...[  STARTING ]");

    bundleContext = bc;

    PopupMessageHandler handler = null;
    handler = new PopupMessageHandlerSwingImpl();

    getConfigurationService();

    bc.registerService(PopupMessageHandler.class.getName(), handler, null);

    if (logger.isInfoEnabled()) logger.info("Swing Notification ...[REGISTERED]");
  }
  /**
   * Returns the <tt>URL</tt> of the sound corresponding to the given property key.
   *
   * @return the <tt>URL</tt> of the sound corresponding to the given property key.
   */
  public URL getSoundURL(String urlKey) {
    String path = getSoundPath(urlKey);

    if (path == null || path.length() == 0) {
      logger.warn("Missing resource for key: " + urlKey);
      return null;
    }
    return getSoundURLForPath(path);
  }
  /**
   * Returns the string representation of the color corresponding to the given key.
   *
   * @param key The key of the color in the colors properties file.
   * @return the string representation of the color corresponding to the given key.
   */
  public String getColorString(String key) {
    String res = getColorResources().get(key);

    if (res == null) {
      logger.error("Missing color resource for key: " + key);

      return "0xFFFFFF";
    } else return res;
  }
  /**
   * Returns the int representation of the color corresponding to the given key.
   *
   * @param key The key of the color in the colors properties file.
   * @return the int representation of the color corresponding to the given key.
   */
  public int getColor(String key) {
    String res = getColorResources().get(key);

    if (res == null) {
      logger.error("Missing color resource for key: " + key);

      return 0xFFFFFF;
    } else return Integer.parseInt(res, 16);
  }
  /**
   * Returns the <tt>InputStream</tt> of the image corresponding to the given key.
   *
   * @param streamKey The identifier of the image in the resource properties file.
   * @return the <tt>InputStream</tt> of the image corresponding to the given key.
   */
  public InputStream getImageInputStream(String streamKey) {
    String path = getImagePath(streamKey);

    if (path == null || path.length() == 0) {
      logger.warn("Missing resource for key: " + streamKey);
      return null;
    }

    return getImageInputStreamForPath(path);
  }
  /**
   * Loads an image from a given image identifier.
   *
   * @param imageID The identifier of the image.
   * @return The image for the given identifier.
   */
  @Override
  public byte[] getImageInBytes(String imageID) {
    InputStream in = getImageInputStream(imageID);

    if (in == null) return null;

    byte[] image = null;

    try {
      image = new byte[in.available()];
      in.read(image);
    } catch (IOException e) {
      logger.error("Failed to load image:" + imageID, e);
    }

    return image;
  }
示例#8
0
/**
 * The <tt>OneToOneCallPeerPanel</tt> is the panel containing data for a call peer in a given call.
 * It contains information like call peer name, photo, call duration, etc.
 *
 * @author Yana Stamcheva
 * @author Lyubomir Marinov
 * @author Sebastien Vincent
 * @author Adam Netocny
 */
public class OneToOneCallPeerPanel extends TransparentPanel
    implements SwingCallPeerRenderer, PropertyChangeListener, Skinnable {
  /**
   * The <tt>Logger</tt> used by the <tt>OneToOneCallPeerPanel</tt> class and its instances for
   * logging output.
   */
  private static final Logger logger = Logger.getLogger(OneToOneCallPeerPanel.class);

  /** Serial version UID. */
  private static final long serialVersionUID = 0L;

  /** The <tt>CallPeer</tt>, which is rendered in this panel. */
  private final CallPeer callPeer;

  /** The <tt>Call</tt>, which is rendered in this panel. */
  private final Call call;

  /**
   * The <tt>CallPeerAdapter</tt> which implements common <tt>CallPeer</tt>-related listeners on
   * behalf of this instance.
   */
  private final CallPeerAdapter callPeerAdapter;

  /** The renderer of the call. */
  private final SwingCallRenderer callRenderer;

  /** The component showing the status of the underlying call peer. */
  private final JLabel callStatusLabel = new JLabel();

  /** The center component. */
  private final VideoContainer center;

  /**
   * The AWT <tt>Component</tt> which implements a button which allows closing/hiding the visual
   * <tt>Component</tt> which depicts the video streaming from the local peer/user to the remote
   * peer(s).
   */
  private Component closeLocalVisualComponentButton;

  /**
   * A listener to desktop sharing granted/revoked events and to mouse and keyboard interaction with
   * the remote video displaying the remote desktop.
   */
  private final DesktopSharingMouseAndKeyboardListener desktopSharingMouseAndKeyboardListener;

  /**
   * The indicator which determines whether {@link #dispose()} has already been invoked on this
   * instance. If <tt>true</tt>, this instance is considered non-functional and is to be left to the
   * garbage collector.
   */
  private boolean disposed = false;

  /** The DTMF label. */
  private final JLabel dtmfLabel = new JLabel();

  /** The component responsible for displaying an error message. */
  private JTextComponent errorMessageComponent;

  /** The label showing whether the call is on or off hold. */
  private final JLabel holdStatusLabel = new JLabel();

  /** Sound local level label. */
  private InputVolumeControlButton localLevel;

  /**
   * The <tt>Component</tt> which {@link #updateViewFromModelInEventDispatchThread()} last added to
   * {@link #center} as the visual <tt>Component</tt> displaying the video streaming from the local
   * peer/user to the remote peer(s).
   *
   * <p><b>Warning</b>: It is not to be used for any other purposes because it may not represent the
   * current state of the model of this view.
   */
  private Component localVideo;

  /** The label showing whether the voice has been set to mute. */
  private final JLabel muteStatusLabel = new JLabel();

  /** The <tt>Icon</tt> which represents the avatar of the associated call peer. */
  private ImageIcon peerImage;

  /** The name of the peer. */
  private String peerName;

  /** The label containing the user photo. */
  private final JLabel photoLabel;

  /** Sound remote level label. */
  private Component remoteLevel;

  /**
   * The <tt>Component</tt> which {@link #updateViewFromModelInEventDispatchThread()} last added to
   * {@link #center} as the visual <tt>Component</tt> displaying the video streaming from the remote
   * peer(s) to the local peer/user.
   *
   * <p><b>Warning</b>: It is not to be used for any other purposes because it may not represent the
   * current state of the model of this view.
   */
  private Component remoteVideo;

  /** Current id for security image. */
  private ImageID securityImageID = ImageLoader.SECURE_BUTTON_OFF;

  /** The panel containing security related components. */
  private SecurityPanel<?> securityPanel;

  /** The security status of the peer */
  private final SecurityStatusLabel securityStatusLabel = new SecurityStatusLabel();

  /** The status bar component. */
  private final Component statusBar;

  /** The facility which aids this instance in the dealing with the video-related information. */
  private final UIVideoHandler2 uiVideoHandler;

  /**
   * The <tt>Observer</tt> which listens to changes in the video-related information detected and
   * reported by {@link #uiVideoHandler}.
   */
  private final Observer uiVideoHandlerObserver =
      new Observer() {
        public void update(Observable o, Object arg) {
          updateViewFromModel();
        }
      };

  /**
   * The <tt>Runnable</tt> which is scheduled by {@link #updateViewFromModel()} for execution in the
   * AWT event dispatching thread in order to invoke {@link
   * #updateViewFromModelInEventDispatchThread()}.
   */
  private final Runnable updateViewFromModelInEventDispatchThread =
      new Runnable() {
        public void run() {
          /*
           * 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.
           */
          if (!disposed) updateViewFromModelInEventDispatchThread();
        }
      };

  /**
   * Creates a <tt>CallPeerPanel</tt> for the given call peer.
   *
   * @param callRenderer the renderer of the call
   * @param callPeer the <tt>CallPeer</tt> represented in this panel
   * @param uiVideoHandler the facility which is to aid the new instance in the dealing with the
   *     video-related information
   */
  public OneToOneCallPeerPanel(
      SwingCallRenderer callRenderer, CallPeer callPeer, UIVideoHandler2 uiVideoHandler) {
    this.callRenderer = callRenderer;
    this.callPeer = callPeer;
    // we need to obtain call as soon as possible
    // cause if it fails too quickly we may fail to show it
    this.call = callPeer.getCall();
    this.uiVideoHandler = uiVideoHandler;

    peerName = CallManager.getPeerDisplayName(callPeer);
    securityPanel = SecurityPanel.create(this, callPeer, null);

    photoLabel = new JLabel(getPhotoLabelIcon());
    center = createCenter();
    statusBar = createStatusBar();

    setPeerImage(CallManager.getPeerImage(callPeer));

    /* Lay out the main Components of the UI. */
    setLayout(new GridBagLayout());

    GridBagConstraints cnstrnts = new GridBagConstraints();

    if (center != null) {
      cnstrnts.fill = GridBagConstraints.BOTH;
      cnstrnts.gridx = 0;
      cnstrnts.gridy = 1;
      cnstrnts.weightx = 1;
      cnstrnts.weighty = 1;
      add(center, cnstrnts);
    }
    if (statusBar != null) {
      cnstrnts.fill = GridBagConstraints.NONE;
      cnstrnts.gridx = 0;
      cnstrnts.gridy = 3;
      cnstrnts.weightx = 0;
      cnstrnts.weighty = 0;
      cnstrnts.insets = new Insets(5, 0, 0, 0);
      add(statusBar, cnstrnts);
    }

    createSoundLevelIndicators();
    initSecuritySettings();

    /*
     * Add the listeners which will be notified about changes in the model
     * and which will update this view.
     */
    callPeerAdapter = new CallPeerAdapter(callPeer, this);
    uiVideoHandler.addObserver(uiVideoHandlerObserver);

    /*
     * This view adapts to whether it is displayed in full-screen or
     * windowed mode.
     */
    if (callRenderer instanceof Component) {
      ((Component) callRenderer).addPropertyChangeListener(CallContainer.PROP_FULL_SCREEN, this);
    }

    OperationSetDesktopSharingClient desktopSharingClient =
        callPeer.getProtocolProvider().getOperationSet(OperationSetDesktopSharingClient.class);
    if (desktopSharingClient != null) {
      desktopSharingMouseAndKeyboardListener =
          new DesktopSharingMouseAndKeyboardListener(callPeer, desktopSharingClient);
    } else desktopSharingMouseAndKeyboardListener = null;

    updateViewFromModel();
  }

  /**
   * Creates the <tt>Component</tt> hierarchy of the central area of this <tt>CallPeerPanel</tt>
   * which displays the photo of the <tt>CallPeer</tt> or the video if any.
   */
  private VideoContainer createCenter() {
    photoLabel.setPreferredSize(new Dimension(90, 90));

    return createVideoContainer(photoLabel);
  }

  /** Creates sound level related components. */
  private void createSoundLevelIndicators() {
    TransparentPanel localLevelPanel = new TransparentPanel(new BorderLayout(5, 0));
    TransparentPanel remoteLevelPanel = new TransparentPanel(new BorderLayout(5, 0));

    localLevel =
        new InputVolumeControlButton(
            call, ImageLoader.MICROPHONE, ImageLoader.MUTE_BUTTON, false, false);
    remoteLevel =
        new OutputVolumeControlButton(call.getConference(), ImageLoader.HEADPHONE, false, false)
            .getComponent();

    final SoundLevelIndicator localLevelIndicator =
        new SoundLevelIndicator(SoundLevelChangeEvent.MIN_LEVEL, SoundLevelChangeEvent.MAX_LEVEL);
    final SoundLevelIndicator remoteLevelIndicator =
        new SoundLevelIndicator(SoundLevelChangeEvent.MIN_LEVEL, SoundLevelChangeEvent.MAX_LEVEL);

    localLevelPanel.add(localLevel, BorderLayout.WEST);
    localLevelPanel.add(localLevelIndicator, BorderLayout.CENTER);
    remoteLevelPanel.add(remoteLevel, BorderLayout.WEST);
    remoteLevelPanel.add(remoteLevelIndicator, BorderLayout.CENTER);

    GridBagConstraints constraints = new GridBagConstraints();
    constraints.fill = GridBagConstraints.NONE;
    constraints.gridx = 0;
    constraints.gridy = 5;
    constraints.weightx = 0;
    constraints.weighty = 0;
    constraints.insets = new Insets(10, 0, 0, 0);

    add(localLevelPanel, constraints);

    constraints.fill = GridBagConstraints.NONE;
    constraints.gridx = 0;
    constraints.gridy = 6;
    constraints.weightx = 0;
    constraints.weighty = 0;
    constraints.insets = new Insets(5, 0, 10, 0);

    add(remoteLevelPanel, constraints);

    if (!GuiActivator.getConfigurationService()
        .getBoolean(
            "net.java.sip.communicator.impl.gui.main.call." + "DISABLE_SOUND_LEVEL_INDICATORS",
            false)) {
      callPeer.addStreamSoundLevelListener(
          new SoundLevelListener() {
            public void soundLevelChanged(Object source, int level) {
              remoteLevelIndicator.updateSoundLevel(level);
            }
          });
      /*
       * By the time the UI gets to be initialized, the callPeer may have
       * been removed from its Call. As far as the UI is concerned, the
       * callPeer will never have a Call again and there will be no audio
       * levels to display anyway so there is no point in throwing a
       * NullPointerException here.
       */
      if (call != null) {
        call.addLocalUserSoundLevelListener(
            new SoundLevelListener() {
              public void soundLevelChanged(Object source, int level) {
                localLevelIndicator.updateSoundLevel(level);
              }
            });
      }
    }
  }

  /**
   * Creates the <tt>Component</tt> hierarchy of the area of status-related information such as
   * <tt>CallPeer</tt> display name, call duration, security status.
   *
   * @return the root of the <tt>Component</tt> hierarchy of the area of status-related information
   *     such as <tt>CallPeer</tt> display name, call duration, security status
   */
  private Component createStatusBar() {
    // stateLabel
    callStatusLabel.setForeground(Color.WHITE);
    dtmfLabel.setForeground(Color.WHITE);
    callStatusLabel.setText(callPeer.getState().getLocalizedStateString());

    PeerStatusPanel statusPanel = new PeerStatusPanel(new GridBagLayout());

    GridBagConstraints constraints = new GridBagConstraints();

    constraints.gridx = 0;
    constraints.gridy = 0;
    statusPanel.add(securityStatusLabel, constraints);
    initSecurityStatusLabel();

    constraints.gridx++;
    statusPanel.add(holdStatusLabel, constraints);

    constraints.gridx++;
    statusPanel.add(muteStatusLabel, constraints);

    constraints.gridx++;
    callStatusLabel.setBorder(BorderFactory.createEmptyBorder(2, 3, 2, 12));
    statusPanel.add(callStatusLabel, constraints);

    constraints.gridx++;
    constraints.weightx = 1f;
    statusPanel.add(dtmfLabel, constraints);

    return statusPanel;
  }

  /**
   * Creates a new AWT <tt>Container</tt> which can display a single <tt>Component</tt> at a time
   * (supposedly, one which represents video) and, in the absence of such a <tt>Component</tt>,
   * displays a predefined default <tt>Component</tt> (in accord with the previous supposition, one
   * which is the default when there is no video). The returned <tt>Container</tt> will track the
   * <tt>Components</tt>s added to and removed from it in order to make sure that
   * <tt>noVideoContainer</tt> is displayed as described.
   *
   * @param noVideoComponent the predefined default <tt>Component</tt> to be displayed in the
   *     returned <tt>Container</tt> when there is no other <tt>Component</tt> in it
   * @return a new <tt>Container</tt> which can display a single <tt>Component</tt> at a time and,
   *     in the absence of such a <tt>Component</tt>, displays <tt>noVideoComponent</tt>
   */
  private VideoContainer createVideoContainer(Component noVideoComponent) {
    Container oldParent = noVideoComponent.getParent();

    if (oldParent != null) oldParent.remove(noVideoComponent);

    return new VideoContainer(noVideoComponent, false);
  }

  /**
   * Releases the resources acquired by this instance which require explicit disposal (e.g. any
   * listeners added to the depicted <tt>CallPeer</tt>. Invoked by <tt>OneToOneCallPanel</tt> when
   * it determines that this <tt>OneToOneCallPeerPanel</tt> is no longer necessary.
   */
  public void dispose() {
    disposed = true;

    callPeerAdapter.dispose();
    uiVideoHandler.deleteObserver(uiVideoHandlerObserver);

    if (callRenderer instanceof Component) {
      ((Component) callRenderer).removePropertyChangeListener(CallContainer.PROP_FULL_SCREEN, this);
    }
  }

  /**
   * Returns the parent <tt>CallPanel</tt> containing this renderer.
   *
   * @return the parent <tt>CallPanel</tt> containing this renderer
   */
  public CallPanel getCallPanel() {
    return callRenderer.getCallContainer();
  }

  /**
   * Returns the parent call renderer.
   *
   * @return the parent call renderer
   */
  public CallRenderer getCallRenderer() {
    return callRenderer;
  }

  /**
   * Returns the component associated with this renderer.
   *
   * @return the component associated with this renderer
   */
  public Component getComponent() {
    return this;
  }

  /**
   * Returns the name of the peer, contained in this panel.
   *
   * @return the name of the peer, contained in this panel
   */
  public String getPeerName() {
    return peerName;
  }

  /**
   * Gets the <tt>Icon</tt> to be displayed in {@link #photoLabel}.
   *
   * @return the <tt>Icon</tt> to be displayed in {@link #photoLabel}
   */
  private ImageIcon getPhotoLabelIcon() {
    return (peerImage == null)
        ? new ImageIcon(ImageLoader.getImage(ImageLoader.DEFAULT_USER_PHOTO))
        : peerImage;
  }

  /** Initializes the security settings for this call peer. */
  private void initSecuritySettings() {
    CallPeerSecurityStatusEvent securityEvent = callPeer.getCurrentSecuritySettings();

    if (securityEvent instanceof CallPeerSecurityOnEvent)
      securityOn((CallPeerSecurityOnEvent) securityEvent);
  }

  /** Initializes the security status label, shown in the call status bar. */
  private void initSecurityStatusLabel() {
    securityStatusLabel.setBorder(BorderFactory.createEmptyBorder(2, 5, 2, 5));

    securityStatusLabel.addMouseListener(
        new MouseAdapter() {
          /** Invoked when a mouse button has been pressed on a component. */
          @Override
          public void mousePressed(MouseEvent e) {
            // Only show the security details if the security is on.
            SrtpControl ctrl = securityPanel.getSecurityControl();
            if (ctrl instanceof ZrtpControl && ctrl.getSecureCommunicationStatus()) {
              setSecurityPanelVisible(
                  !callRenderer
                      .getCallContainer()
                      .getCallWindow()
                      .getFrame()
                      .getGlassPane()
                      .isVisible());
            }
          }
        });
  }

  /**
   * Determines whether the visual <tt>Component</tt> depicting the video streaming from the local
   * peer/user to the remote peer(s) is currently visible.
   *
   * @return <tt>true</tt> if the visual <tt>Component</tt> depicting the video streaming from the
   *     local peer/user to the remote peer(s) is currently visible; otherwise, <tt>false</tt>
   */
  public boolean isLocalVideoVisible() {
    return uiVideoHandler.isLocalVideoVisible();
  }

  /** Reloads all icons. */
  public void loadSkin() {
    if (localLevel != null)
      localLevel.setIcon(new ImageIcon(ImageLoader.getImage(ImageLoader.MICROPHONE)));

    if (remoteLevel != null && remoteLevel instanceof Skinnable)
      ((Skinnable) remoteLevel).loadSkin();

    if (muteStatusLabel.getIcon() != null)
      muteStatusLabel.setIcon(new ImageIcon(ImageLoader.getImage(ImageLoader.MUTE_STATUS_ICON)));

    if (holdStatusLabel.getIcon() != null)
      holdStatusLabel.setIcon(new ImageIcon(ImageLoader.getImage(ImageLoader.HOLD_STATUS_ICON)));

    securityStatusLabel.setIcon(new ImageIcon(ImageLoader.getImage(securityImageID)));

    if (peerImage == null) {
      photoLabel.setIcon(new ImageIcon(ImageLoader.getImage(ImageLoader.DEFAULT_USER_PHOTO)));
    }
  }

  /**
   * Prints the given DTMG character through this <tt>CallPeerRenderer</tt>.
   *
   * @param dtmfChar the DTMF char to print
   */
  public void printDTMFTone(char dtmfChar) {
    dtmfLabel.setText(dtmfLabel.getText() + dtmfChar);
    if (dtmfLabel.getBorder() == null)
      dtmfLabel.setBorder(BorderFactory.createEmptyBorder(2, 1, 2, 5));
  }

  /**
   * Notifies this instance about a change in the value of a property of a source which of interest
   * to this instance. For example, <tt>OneToOneCallPeerPanel</tt> updates its user
   * interface-related properties upon changes in the value of the {@link
   * CallContainer#PROP_FULL_SCREEN} property of its associated {@link #callRenderer}.
   *
   * @param ev a <tt>PropertyChangeEvent</tt> which identifies the source, the name of the property
   *     and the old and new values
   */
  public void propertyChange(PropertyChangeEvent ev) {
    if (CallContainer.PROP_FULL_SCREEN.equals(ev.getPropertyName())) updateViewFromModel();
  }

  /**
   * Re-dispatches glass pane mouse events only in case they occur on the security panel.
   *
   * @param glassPane the glass pane
   * @param e the mouse event in question
   */
  private void redispatchMouseEvent(Component glassPane, MouseEvent e) {
    Point glassPanePoint = e.getPoint();

    Point securityPanelPoint =
        SwingUtilities.convertPoint(glassPane, glassPanePoint, securityPanel);

    Component component;
    Point componentPoint;

    if (securityPanelPoint.y > 0) {
      component = securityPanel;
      componentPoint = securityPanelPoint;
    } else {
      Container contentPane =
          callRenderer.getCallContainer().getCallWindow().getFrame().getContentPane();

      Point containerPoint = SwingUtilities.convertPoint(glassPane, glassPanePoint, contentPane);

      component =
          SwingUtilities.getDeepestComponentAt(contentPane, containerPoint.x, containerPoint.y);

      componentPoint = SwingUtilities.convertPoint(contentPane, glassPanePoint, component);
    }

    if (component != null)
      component.dispatchEvent(
          new MouseEvent(
              component,
              e.getID(),
              e.getWhen(),
              e.getModifiers(),
              componentPoint.x,
              componentPoint.y,
              e.getClickCount(),
              e.isPopupTrigger()));

    e.consume();
  }

  /**
   * The handler for the security event received. The security event for starting establish a secure
   * connection.
   *
   * @param evt the security started event received
   */
  public void securityNegotiationStarted(CallPeerSecurityNegotiationStartedEvent evt) {
    if (Boolean.parseBoolean(
        GuiActivator.getResources().getSettingsString("impl.gui.PARANOIA_UI"))) {
      SrtpControl srtpControl = null;
      if (callPeer instanceof MediaAwareCallPeer) srtpControl = evt.getSecurityController();

      securityPanel = new ParanoiaTimerSecurityPanel<SrtpControl>(srtpControl);

      setSecurityPanelVisible(true);
    }
  }

  /**
   * Indicates that the security has gone off.
   *
   * @param evt the <tt>CallPeerSecurityOffEvent</tt> that notified us
   */
  public void securityOff(final CallPeerSecurityOffEvent evt) {
    if (!SwingUtilities.isEventDispatchThread()) {
      SwingUtilities.invokeLater(
          new Runnable() {
            public void run() {
              securityOff(evt);
            }
          });
      return;
    }

    if (evt.getSessionType() == CallPeerSecurityOffEvent.AUDIO_SESSION) {
      securityStatusLabel.setText("");
      securityStatusLabel.setSecurityOff();
      if (securityStatusLabel.getBorder() == null)
        securityStatusLabel.setBorder(BorderFactory.createEmptyBorder(2, 5, 2, 3));
    }

    securityPanel.securityOff(evt);
  }

  /**
   * Indicates that the security is turned on.
   *
   * <p>Sets the secured status icon to the status panel and initializes/updates the corresponding
   * security details.
   *
   * @param evt Details about the event that caused this message.
   */
  public void securityOn(final CallPeerSecurityOnEvent evt) {
    if (!SwingUtilities.isEventDispatchThread()) {
      SwingUtilities.invokeLater(
          new Runnable() {
            public void run() {
              securityOn(evt);
            }
          });
      return;
    }

    // If the securityOn is called without a specific event, we'll just set
    // the security label status to on.
    if (evt == null) {
      securityStatusLabel.setSecurityOn();
      return;
    }

    SrtpControl srtpControl = evt.getSecurityController();

    if (!srtpControl.requiresSecureSignalingTransport()
        || callPeer.getProtocolProvider().isSignalingTransportSecure()) {
      if (srtpControl instanceof ZrtpControl) {
        securityStatusLabel.setText("zrtp");

        if (!((ZrtpControl) srtpControl).isSecurityVerified())
          securityStatusLabel.setSecurityPending();
        else securityStatusLabel.setSecurityOn();
      } else securityStatusLabel.setSecurityOn();
    }

    // if we have some other panel, using other control
    if (!srtpControl.getClass().isInstance(securityPanel.getSecurityControl())
        || (securityPanel instanceof ParanoiaTimerSecurityPanel)) {
      setSecurityPanelVisible(false);

      securityPanel = SecurityPanel.create(this, callPeer, srtpControl);

      if (srtpControl instanceof ZrtpControl)
        ((ZrtpSecurityPanel) securityPanel).setSecurityStatusLabel(securityStatusLabel);
    }

    securityPanel.securityOn(evt);

    boolean isSecurityLowPriority =
        Boolean.parseBoolean(
            GuiActivator.getResources()
                .getSettingsString("impl.gui.I_DONT_CARE_THAT_MUCH_ABOUT_SECURITY"));

    // Display ZRTP panel in case SAS was not verified or a AOR mismtach
    // was detected during creation of ZrtpSecurityPanel.
    // Don't show panel if user does not care about security at all.
    if (srtpControl instanceof ZrtpControl
        && !isSecurityLowPriority
        && (!((ZrtpControl) srtpControl).isSecurityVerified()
            || ((ZrtpSecurityPanel) securityPanel).isZidAorMismatch())) {
      setSecurityPanelVisible(true);
    }

    this.revalidate();
  }

  /** Indicates that the security status is pending confirmation. */
  public void securityPending() {
    securityStatusLabel.setSecurityPending();
  }

  /**
   * Indicates that the security is timeouted, is not supported by the other end.
   *
   * @param evt Details about the event that caused this message.
   */
  public void securityTimeout(final CallPeerSecurityTimeoutEvent evt) {
    if (!SwingUtilities.isEventDispatchThread()) {
      SwingUtilities.invokeLater(
          new Runnable() {
            public void run() {
              securityTimeout(evt);
            }
          });
      return;
    }

    if (securityPanel != null) securityPanel.securityTimeout(evt);
  }

  /**
   * Sets the reason of a call failure if one occurs. The renderer should display this reason to the
   * user.
   *
   * @param reason the reason to display
   */
  public void setErrorReason(final String reason) {
    if (!SwingUtilities.isEventDispatchThread()) {
      SwingUtilities.invokeLater(
          new Runnable() {
            public void run() {
              setErrorReason(reason);
            }
          });
      return;
    }

    if (errorMessageComponent == null) {
      errorMessageComponent = new JTextPane();

      JTextPane textPane = (JTextPane) errorMessageComponent;
      textPane.setEditable(false);
      textPane.setOpaque(false);

      StyledDocument doc = textPane.getStyledDocument();

      MutableAttributeSet standard = new SimpleAttributeSet();
      StyleConstants.setAlignment(standard, StyleConstants.ALIGN_CENTER);
      StyleConstants.setFontFamily(standard, textPane.getFont().getFamily());
      StyleConstants.setFontSize(standard, 12);
      doc.setParagraphAttributes(0, 0, standard, true);

      GridBagConstraints constraints = new GridBagConstraints();
      constraints.fill = GridBagConstraints.HORIZONTAL;
      constraints.gridx = 0;
      constraints.gridy = 4;
      constraints.weightx = 1;
      constraints.weighty = 0;
      constraints.insets = new Insets(5, 0, 0, 0);

      add(errorMessageComponent, constraints);
      this.revalidate();
    }

    errorMessageComponent.setText(reason);

    if (isVisible()) errorMessageComponent.repaint();
  }

  /**
   * Shows/hides the visual <tt>Component</tt> depicting the video streaming from the local
   * peer/user to the remote peer(s).
   *
   * @param visible <tt>true</tt> to show the visual <tt>Component</tt> depicting the video
   *     streaming from the local peer/user to the remote peer(s); <tt>false</tt>, otherwise
   */
  public void setLocalVideoVisible(boolean visible) {
    uiVideoHandler.setLocalVideoVisible(visible);
  }

  /**
   * Sets the mute status icon to the status panel.
   *
   * @param isMute indicates if the call with this peer is muted
   */
  public void setMute(final boolean isMute) {
    if (!SwingUtilities.isEventDispatchThread()) {
      SwingUtilities.invokeLater(
          new Runnable() {
            public void run() {
              setMute(isMute);
            }
          });
      return;
    }

    if (isMute) {
      muteStatusLabel.setIcon(new ImageIcon(ImageLoader.getImage(ImageLoader.MUTE_STATUS_ICON)));
      muteStatusLabel.setBorder(BorderFactory.createEmptyBorder(2, 3, 2, 3));
    } else {
      muteStatusLabel.setIcon(null);
      muteStatusLabel.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0));
    }

    // Update input volume control button state to reflect the current
    // mute status.
    if (localLevel.isSelected() != isMute) localLevel.setSelected(isMute);

    this.revalidate();
    this.repaint();
  }

  /**
   * Sets the "on hold" property value.
   *
   * @param isOnHold indicates if the call with this peer is put on hold
   */
  public void setOnHold(final boolean isOnHold) {
    if (!SwingUtilities.isEventDispatchThread()) {
      SwingUtilities.invokeLater(
          new Runnable() {
            public void run() {
              setOnHold(isOnHold);
            }
          });
      return;
    }

    if (isOnHold) {
      holdStatusLabel.setIcon(new ImageIcon(ImageLoader.getImage(ImageLoader.HOLD_STATUS_ICON)));
      holdStatusLabel.setBorder(BorderFactory.createEmptyBorder(2, 3, 2, 3));
    } else {
      holdStatusLabel.setIcon(null);
      holdStatusLabel.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0));
    }

    this.revalidate();
    this.repaint();
  }

  /**
   * Set the image of the peer
   *
   * @param image new image
   */
  public void setPeerImage(byte[] image) {
    // If the image is still null we try to obtain it from one of the
    // available contact sources.
    if (image == null || image.length <= 0) {
      GuiActivator.getContactList().setSourceContactImage(peerName, photoLabel, 100, 100);
    } else {
      peerImage = ImageUtils.getScaledRoundedIcon(image, 100, 100);
      if (peerImage == null) peerImage = getPhotoLabelIcon();

      if (!SwingUtilities.isEventDispatchThread()) {
        SwingUtilities.invokeLater(
            new Runnable() {
              public void run() {
                photoLabel.setIcon(peerImage);
                photoLabel.repaint();
              }
            });
      } else {
        photoLabel.setIcon(peerImage);
        photoLabel.repaint();
      }
    }
  }

  /**
   * Sets the name of the peer.
   *
   * @param name the name of the peer
   */
  public void setPeerName(final String name) {
    if (!SwingUtilities.isEventDispatchThread()) {
      SwingUtilities.invokeLater(
          new Runnable() {
            public void run() {
              setPeerName(name);
            }
          });
      return;
    }

    peerName = name;

    ((OneToOneCallPanel) callRenderer).setPeerName(name);
  }

  /**
   * Sets the state of the contained call peer by specifying the state name.
   *
   * @param oldState the previous state of the peer
   * @param newState the new state of the peer
   * @param stateString the state of the contained call peer
   */
  public void setPeerState(
      final CallPeerState oldState, final CallPeerState newState, final String stateString) {
    if (!SwingUtilities.isEventDispatchThread()) {
      SwingUtilities.invokeLater(
          new Runnable() {
            public void run() {
              setPeerState(oldState, newState, stateString);
            }
          });
      return;
    }

    this.callStatusLabel.setText(stateString);

    if (newState == CallPeerState.CONNECTED
        && !CallPeerState.isOnHold(oldState)
        && !securityStatusLabel.isSecurityStatusSet()) {
      securityStatusLabel.setSecurityOff();
    }
  }

  /**
   * Shows/hides the security panel.
   *
   * @param isVisible <tt>true</tt> to show the security panel, <tt>false</tt> to hide it
   */
  public void setSecurityPanelVisible(final boolean isVisible) {
    if (!SwingUtilities.isEventDispatchThread()) {
      SwingUtilities.invokeLater(
          new Runnable() {
            public void run() {
              setSecurityPanelVisible(isVisible);
            }
          });
      return;
    }

    final JFrame callFrame = callRenderer.getCallContainer().getCallWindow().getFrame();

    final JPanel glassPane = (JPanel) callFrame.getGlassPane();

    if (!isVisible) {
      // Need to hide the security panel explicitly in order to keep the
      // fade effect.
      securityPanel.setVisible(false);
      glassPane.setVisible(false);
      glassPane.removeAll();
    } else {
      glassPane.setLayout(null);
      glassPane.addMouseListener(
          new MouseListener() {
            public void mouseClicked(MouseEvent e) {
              redispatchMouseEvent(glassPane, e);
            }

            public void mouseEntered(MouseEvent e) {
              redispatchMouseEvent(glassPane, e);
            }

            public void mouseExited(MouseEvent e) {
              redispatchMouseEvent(glassPane, e);
            }

            public void mousePressed(MouseEvent e) {
              redispatchMouseEvent(glassPane, e);
            }

            public void mouseReleased(MouseEvent e) {
              redispatchMouseEvent(glassPane, e);
            }
          });

      Point securityLabelPoint = securityStatusLabel.getLocation();

      Point newPoint =
          SwingUtilities.convertPoint(
              securityStatusLabel.getParent(),
              securityLabelPoint.x,
              securityLabelPoint.y,
              callFrame);

      securityPanel.setBeginPoint(new Point((int) newPoint.getX() + 15, 0));
      securityPanel.setBounds(0, (int) newPoint.getY() - 5, this.getWidth(), 130);

      glassPane.add(securityPanel);
      // Need to show the security panel explicitly in order to keep the
      // fade effect.
      securityPanel.setVisible(true);
      glassPane.setVisible(true);

      glassPane.addComponentListener(
          new ComponentAdapter() {
            /** Invoked when the component's size changes. */
            @Override
            public void componentResized(ComponentEvent e) {
              if (glassPane.isVisible()) {
                glassPane.setVisible(false);
                callFrame.removeComponentListener(this);
              }
            }
          });
    }
  }

  /**
   * Updates this view i.e. <tt>OneToOneCallPeerPanel</tt> so that it depicts the current state of
   * its model i.e. <tt>callPeer</tt>.
   */
  private void updateViewFromModel() {
    /*
     * 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) {
      if (SwingUtilities.isEventDispatchThread()) updateViewFromModelInEventDispatchThread();
      else {
        SwingUtilities.invokeLater(updateViewFromModelInEventDispatchThread);
      }
    }
  }

  /**
   * 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;
          }
        }
      }
    }
  }

  /** The <tt>TransparentPanel</tt> that will display the peer status. */
  private static class PeerStatusPanel extends TransparentPanel {
    /**
     * Silence the serial warning. Though there isn't a plan to serialize the instances of the
     * class, there're no fields so the default serialization routine will work.
     */
    private static final long serialVersionUID = 0L;

    /**
     * Constructs a new <tt>PeerStatusPanel</tt>.
     *
     * @param layout the <tt>LayoutManager</tt> to use
     */
    public PeerStatusPanel(LayoutManager layout) {
      super(layout);
    }

    /** @{inheritDoc} */
    @Override
    public void paintComponent(Graphics g) {
      super.paintComponent(g);

      g = g.create();

      try {
        AntialiasingManager.activateAntialiasing(g);

        g.setColor(Color.DARK_GRAY);
        g.fillRoundRect(0, 0, this.getWidth(), this.getHeight(), 10, 10);
      } finally {
        g.dispose();
      }
    }
  }
}
示例#9
0
  /**
   * 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;
          }
        }
      }
    }
  }
示例#10
0
/**
 * Activator for the swing notification service.
 *
 * @author Symphorien Wanko
 */
public class SwingNotificationActivator implements BundleActivator {
  /** The bundle context in which we started */
  public static BundleContext bundleContext;

  /** A reference to the configuration service. */
  private static ConfigurationService configService;

  /** Logger for this class. */
  private static final Logger logger = Logger.getLogger(SwingNotificationActivator.class);

  /** A reference to the resource management service. */
  private static ResourceManagementService resourcesService;

  /**
   * Start the swing notification service
   *
   * @param bc
   * @throws java.lang.Exception
   */
  public void start(BundleContext bc) throws Exception {
    if (logger.isInfoEnabled()) logger.info("Swing Notification ...[  STARTING ]");

    bundleContext = bc;

    PopupMessageHandler handler = null;
    handler = new PopupMessageHandlerSwingImpl();

    getConfigurationService();

    bc.registerService(PopupMessageHandler.class.getName(), handler, null);

    if (logger.isInfoEnabled()) logger.info("Swing Notification ...[REGISTERED]");
  }

  public void stop(BundleContext arg0) throws Exception {}

  /**
   * Returns the <tt>ConfigurationService</tt> obtained from the bundle context.
   *
   * @return the <tt>ConfigurationService</tt> obtained from the bundle context
   */
  public static ConfigurationService getConfigurationService() {
    if (configService == null) {
      ServiceReference configReference =
          bundleContext.getServiceReference(ConfigurationService.class.getName());

      configService = (ConfigurationService) bundleContext.getService(configReference);
    }

    return configService;
  }

  /**
   * Returns the <tt>ResourceManagementService</tt> obtained from the bundle context.
   *
   * @return the <tt>ResourceManagementService</tt> obtained from the bundle context
   */
  public static ResourceManagementService getResources() {
    if (resourcesService == null)
      resourcesService = ResourceManagementServiceUtils.getService(bundleContext);
    return resourcesService;
  }
}
/**
 * A default implementation of the <tt>ResourceManagementService</tt>.
 *
 * @author Damian Minkov
 * @author Yana Stamcheva
 * @author Lubomir Marinov
 * @author Adam Netocny
 */
public class ResourceManagementServiceImpl extends AbstractResourcesService {
  /**
   * The <tt>Logger</tt> used by the <tt>ResourceManagementServiceImpl</tt> class and its instances
   * for logging output.
   */
  private static final Logger logger = Logger.getLogger(ResourceManagementServiceImpl.class);

  /** UI Service reference. */
  private UIService uiService = null;

  /** Initializes already registered default resource packs. */
  ResourceManagementServiceImpl() {
    super(ResourceManagementActivator.bundleContext);

    UIService serv = getUIService();
    if (serv != null) {
      serv.repaintUI();
    }
  }

  /**
   * Returns the <tt>UIService</tt> obtained from the bundle context.
   *
   * @return the <tt>UIService</tt> obtained from the bundle context
   */
  private UIService getUIService() {
    if (uiService == null) {
      uiService =
          ServiceUtils.getService(ResourceManagementActivator.bundleContext, UIService.class);
    }
    return uiService;
  }

  /**
   * Gets a reference to the <tt>UIService</tt> when this one is registered.
   *
   * @param event the <tt>ServiceEvent</tt> that has notified us
   */
  @Override
  public void serviceChanged(ServiceEvent event) {
    super.serviceChanged(event);

    Object sService =
        ResourceManagementActivator.bundleContext.getService(event.getServiceReference());

    if (sService instanceof UIService
        && uiService == null
        && event.getType() == ServiceEvent.REGISTERED) {
      uiService = (UIService) sService;
      uiService.repaintUI();
    } else if (sService instanceof UIService && event.getType() == ServiceEvent.UNREGISTERING) {
      if (uiService != null && uiService.equals(sService)) {
        uiService = null;
      }
    }
  }

  /** Repaints the whole UI when a skin pack has changed. */
  @Override
  protected void onSkinPackChanged() {
    UIService serv = getUIService();
    if (serv != null) {
      serv.repaintUI();
    }
  }

  /**
   * Returns the int representation of the color corresponding to the given key.
   *
   * @param key The key of the color in the colors properties file.
   * @return the int representation of the color corresponding to the given key.
   */
  public int getColor(String key) {
    String res = getColorResources().get(key);

    if (res == null) {
      logger.error("Missing color resource for key: " + key);

      return 0xFFFFFF;
    } else return Integer.parseInt(res, 16);
  }

  /**
   * Returns the string representation of the color corresponding to the given key.
   *
   * @param key The key of the color in the colors properties file.
   * @return the string representation of the color corresponding to the given key.
   */
  public String getColorString(String key) {
    String res = getColorResources().get(key);

    if (res == null) {
      logger.error("Missing color resource for key: " + key);

      return "0xFFFFFF";
    } else return res;
  }

  /**
   * Returns the <tt>InputStream</tt> of the image corresponding to the given path.
   *
   * @param path The path to the image file.
   * @return the <tt>InputStream</tt> of the image corresponding to the given path.
   */
  public InputStream getImageInputStreamForPath(String path) {
    SkinPack skinPack = getSkinPack();
    if (skinPack != null) {
      if (skinPack.getClass().getClassLoader().getResourceAsStream(path) != null) {
        return skinPack.getClass().getClassLoader().getResourceAsStream(path);
      }
    }

    ImagePack imagePack = getImagePack();
    if (path != null && imagePack != null)
      return imagePack.getClass().getClassLoader().getResourceAsStream(path);

    return null;
  }

  /**
   * Returns the <tt>InputStream</tt> of the image corresponding to the given key.
   *
   * @param streamKey The identifier of the image in the resource properties file.
   * @return the <tt>InputStream</tt> of the image corresponding to the given key.
   */
  public InputStream getImageInputStream(String streamKey) {
    String path = getImagePath(streamKey);

    if (path == null || path.length() == 0) {
      logger.warn("Missing resource for key: " + streamKey);
      return null;
    }

    return getImageInputStreamForPath(path);
  }

  /**
   * Returns the <tt>URL</tt> of the image corresponding to the given key.
   *
   * @param urlKey The identifier of the image in the resource properties file.
   * @return the <tt>URL</tt> of the image corresponding to the given key
   */
  public URL getImageURL(String urlKey) {
    String path = getImagePath(urlKey);

    if (path == null || path.length() == 0) {
      if (logger.isInfoEnabled()) logger.info("Missing resource for key: " + urlKey);
      return null;
    }
    return getImageURLForPath(path);
  }

  /**
   * Returns the <tt>URL</tt> of the image corresponding to the given path.
   *
   * @param path The path to the given image file.
   * @return the <tt>URL</tt> of the image corresponding to the given path.
   */
  public URL getImageURLForPath(String path) {
    SkinPack skinPack = getSkinPack();
    if (skinPack != null) {
      if (skinPack.getClass().getClassLoader().getResource(path) != null) {
        return skinPack.getClass().getClassLoader().getResource(path);
      }
    }

    ImagePack imagePack = getImagePack();
    return imagePack.getClass().getClassLoader().getResource(path);
  }

  /**
   * Returns the <tt>URL</tt> of the sound corresponding to the given property key.
   *
   * @return the <tt>URL</tt> of the sound corresponding to the given property key.
   */
  public URL getSoundURL(String urlKey) {
    String path = getSoundPath(urlKey);

    if (path == null || path.length() == 0) {
      logger.warn("Missing resource for key: " + urlKey);
      return null;
    }
    return getSoundURLForPath(path);
  }

  /**
   * Returns the <tt>URL</tt> of the sound corresponding to the given path.
   *
   * @param path the path, for which we're looking for a sound URL
   * @return the <tt>URL</tt> of the sound corresponding to the given path.
   */
  public URL getSoundURLForPath(String path) {
    return getSoundPack().getClass().getClassLoader().getResource(path);
  }

  /**
   * Loads an image from a given image identifier.
   *
   * @param imageID The identifier of the image.
   * @return The image for the given identifier.
   */
  @Override
  public byte[] getImageInBytes(String imageID) {
    InputStream in = getImageInputStream(imageID);

    if (in == null) return null;

    byte[] image = null;

    try {
      image = new byte[in.available()];
      in.read(image);
    } catch (IOException e) {
      logger.error("Failed to load image:" + imageID, e);
    }

    return image;
  }

  /**
   * Loads an image from a given image identifier.
   *
   * @param imageID The identifier of the image.
   * @return The image for the given identifier.
   */
  @Override
  public ImageIcon getImage(String imageID) {
    URL imageURL = getImageURL(imageID);

    return (imageURL == null) ? null : new ImageIcon(imageURL);
  }

  /**
   * Builds a new skin bundle from the zip file content.
   *
   * @param zipFile Zip file with skin information.
   * @return <tt>File</tt> for the bundle.
   * @throws Exception When something goes wrong.
   */
  public File prepareSkinBundleFromZip(File zipFile) throws Exception {
    return SkinJarBuilder.createBundleFromZip(zipFile, getImagePack());
  }

  /**
   * Gets the specified setting from the config service if present, otherwise from the embedded
   * resources (resources/config/defaults.properties).
   *
   * @param key The setting to lookup.
   * @return The setting for the key or {@code null} if not found.
   */
  @Override
  public String getSettingsString(String key) {
    Object configValue = ResourceManagementActivator.getConfigService().getProperty(key);
    if (configValue == null) {
      configValue = super.getSettingsString(key);
    }

    return configValue == null ? null : configValue.toString();
  }
}
/**
 * Implements <tt>BundleActivator</tt> for the neomedia bundle.
 *
 * @author Martin Andre
 * @author Emil Ivov
 * @author Lyubomir Marinov
 * @author Boris Grozev
 */
public class NeomediaActivator implements BundleActivator {

  /**
   * The <tt>Logger</tt> used by the <tt>NeomediaActivator</tt> class and its instances for logging
   * output.
   */
  private final Logger logger = Logger.getLogger(NeomediaActivator.class);

  /** Indicates if the audio configuration form should be disabled, i.e. not visible to the user. */
  private static final String AUDIO_CONFIG_DISABLED_PROP =
      "net.java.sip.communicator.impl.neomedia.AUDIO_CONFIG_DISABLED";

  /** Indicates if the video configuration form should be disabled, i.e. not visible to the user. */
  private static final String VIDEO_CONFIG_DISABLED_PROP =
      "net.java.sip.communicator.impl.neomedia.VIDEO_CONFIG_DISABLED";

  /** Indicates if the H.264 configuration form should be disabled, i.e. not visible to the user. */
  private static final String H264_CONFIG_DISABLED_PROP =
      "net.java.sip.communicator.impl.neomedia.h264config.DISABLED";

  /** Indicates if the ZRTP configuration form should be disabled, i.e. not visible to the user. */
  private static final String ZRTP_CONFIG_DISABLED_PROP =
      "net.java.sip.communicator.impl.neomedia.zrtpconfig.DISABLED";

  /**
   * Indicates if the call recording config form should be disabled, i.e. not visible to the user.
   */
  private static final String CALL_RECORDING_CONFIG_DISABLED_PROP =
      "net.java.sip.communicator.impl.neomedia.callrecordingconfig.DISABLED";

  /**
   * The name of the notification pop-up event displayed when the device configration has changed.
   */
  private static final String DEVICE_CONFIGURATION_HAS_CHANGED = "DeviceConfigurationChanged";

  /**
   * The context in which the one and only <tt>NeomediaActivator</tt> instance has started
   * executing.
   */
  private static BundleContext bundleContext;

  /**
   * The <tt>ConfigurationService</tt> registered in {@link #bundleContext} and used by the
   * <tt>NeomediaActivator</tt> instance to read and write configuration properties.
   */
  private static ConfigurationService configurationService;

  /**
   * The <tt>FileAccessService</tt> registered in {@link #bundleContext} and used by the
   * <tt>NeomediaActivator</tt> instance to safely access files.
   */
  private static FileAccessService fileAccessService;

  /** The notifcation service to pop-up messages. */
  private static NotificationService notificationService;

  /**
   * The one and only <tt>MediaServiceImpl</tt> instance registered in {@link #bundleContext} by the
   * <tt>NeomediaActivator</tt> instance.
   */
  private static MediaServiceImpl mediaServiceImpl;

  /**
   * The <tt>ResourceManagementService</tt> registered in {@link #bundleContext} and representing
   * the resources such as internationalized and localized text and images used by the neomedia
   * bundle.
   */
  private static ResourceManagementService resources;

  /**
   * The OSGi <tt>PacketLoggingService</tt> of {@link #mediaServiceImpl} in {@link #bundleContext}
   * and used for debugging.
   */
  private static PacketLoggingService packetLoggingService = null;

  /** A listener to the click on the popup message concerning device configuration changes. */
  private AudioDeviceConfigurationListener deviceConfigurationPropertyChangeListener;

  /** A {@link MediaConfigurationService} instance. */
  //    private static MediaConfigurationImpl mediaConfiguration;

  /** The audio configuration form used to define the capture/notify/playback audio devices. */
  private static ConfigurationForm audioConfigurationForm;

  /**
   * Starts the execution of the neomedia bundle in the specified context.
   *
   * @param bundleContext the context in which the neomedia bundle is to start executing
   * @throws Exception if an error occurs while starting the execution of the neomedia bundle in the
   *     specified context
   */
  public void start(BundleContext bundleContext) throws Exception {
    if (logger.isDebugEnabled()) logger.debug("Started.");

    NeomediaActivator.bundleContext = bundleContext;

    // MediaService
    mediaServiceImpl = (MediaServiceImpl) LibJitsi.getMediaService();

    bundleContext.registerService(MediaService.class.getName(), mediaServiceImpl, null);
    if (logger.isDebugEnabled()) logger.debug("Media Service ... [REGISTERED]");

    //        mediaConfiguration = new MediaConfigurationImpl();
    //        bundleContext.registerService(
    //                MediaConfigurationService.class.getStatus(),
    //                getMediaConfiguration(),
    //                null);
    if (logger.isDebugEnabled()) logger.debug("Media Configuration ... [REGISTERED]");

    ConfigurationService cfg = NeomediaActivator.getConfigurationService();
    Dictionary<String, String> mediaProps = new Hashtable<String, String>();

    mediaProps.put(ConfigurationForm.FORM_TYPE, ConfigurationForm.GENERAL_TYPE);

    // If the audio configuration form is disabled don't register it.
    //        if ((cfg == null) || !cfg.getBoolean(AUDIO_CONFIG_DISABLED_PROP, false))
    //        {
    //            audioConfigurationForm
    //                = new LazyConfigurationForm(
    //                        AudioConfigurationPanel.class.getStatus(),
    //                        getClass().getClassLoader(),
    //                        "plugin.mediaconfig.AUDIO_ICON",
    //                        "impl.neomedia.configform.AUDIO",
    //                        3);
    //
    //            bundleContext.registerService(
    //                    ConfigurationForm.class.getStatus(),
    //                    audioConfigurationForm,
    //                    mediaProps);
    //
    //            if (deviceConfigurationPropertyChangeListener == null)
    //            {
    //                // Initializes and registers the changed device configuration
    //                // event ot the notification service.
    //                getNotificationService();
    //
    //                deviceConfigurationPropertyChangeListener
    //                    = new AudioDeviceConfigurationListener();
    //                mediaServiceImpl
    //                    .getDeviceConfiguration()
    //                        .addPropertyChangeListener(
    //                                deviceConfigurationPropertyChangeListener);
    //            }
    //        }

    // If the video configuration form is disabled don't register it.
    //        if ((cfg == null) || !cfg.getBoolean(VIDEO_CONFIG_DISABLED_PROP, false))
    //        {
    //            bundleContext.registerService(
    //                    ConfigurationForm.class.getStatus(),
    //                    new LazyConfigurationForm(
    //                            VideoConfigurationPanel.class.getStatus(),
    //                            getClass().getClassLoader(),
    //                            "plugin.mediaconfig.VIDEO_ICON",
    //                            "impl.neomedia.configform.VIDEO",
    //                            4),
    //                    mediaProps);
    //        }

    // H.264
    // If the H.264 configuration form is disabled don't register it.
    //        if ((cfg == null) || !cfg.getBoolean(H264_CONFIG_DISABLED_PROP, false))
    //        {
    //            Dictionary<String, String> h264Props
    //                = new Hashtable<String, String>();
    //
    //            h264Props.put(
    //                    ConfigurationForm.FORM_TYPE,
    //                    ConfigurationForm.ADVANCED_TYPE);
    //            bundleContext.registerService(
    //                    ConfigurationForm.class.getStatus(),
    //                    new LazyConfigurationForm(
    //                            ConfigurationPanel.class.getStatus(),
    //                            getClass().getClassLoader(),
    //                            "plugin.mediaconfig.VIDEO_ICON",
    //                            "impl.neomedia.configform.H264",
    //                            -1,
    //                            true),
    //                    h264Props);
    //        }

    // ZRTP
    // If the ZRTP configuration form is disabled don't register it.
    //        if ((cfg == null) || !cfg.getBoolean(ZRTP_CONFIG_DISABLED_PROP, false))
    //        {
    //            Dictionary<String, String> securityProps
    //                = new Hashtable<String, String>();
    //
    //            securityProps.put( ConfigurationForm.FORM_TYPE,
    //                            ConfigurationForm.SECURITY_TYPE);
    //            bundleContext.registerService(
    //                ConfigurationForm.class.getStatus(),
    //                new LazyConfigurationForm(
    //                    SecurityConfigForm.class.getStatus(),
    //                    getClass().getClassLoader(),
    //                    "impl.media.security.zrtp.CONF_ICON",
    //                    "impl.media.security.zrtp.TITLE",
    //                    0),
    //                securityProps);
    //        }

    // we use the nist-sdp stack to make parse sdp and we need to set the
    // following property to make sure that it would accept java generated
    // IPv6 addresses that contain address scope zones.
    System.setProperty("gov.nist.core.STRIP_ADDR_SCOPES", "true");

    // AudioNotifierService
    AudioNotifierService audioNotifierService = LibJitsi.getAudioNotifierService();

    audioNotifierService.setMute(
        (cfg == null)
            || !cfg.getBoolean("net.java.sip.communicator" + ".impl.sound.isSoundEnabled", true));
    bundleContext.registerService(AudioNotifierService.class.getName(), audioNotifierService, null);

    if (logger.isInfoEnabled()) logger.info("Audio Notifier Service ...[REGISTERED]");

    // Call Recording
    // If the call recording configuration form is disabled don't continue.
    //        if ((cfg == null)
    //                || !cfg.getBoolean(CALL_RECORDING_CONFIG_DISABLED_PROP, false))
    //        {
    //            Dictionary<String, String> callRecordingProps
    //                = new Hashtable<String, String>();
    //
    //            callRecordingProps.put(
    //                    ConfigurationForm.FORM_TYPE,
    //                    ConfigurationForm.ADVANCED_TYPE);
    //            bundleContext.registerService(
    //                    ConfigurationForm.class.getStatus(),
    //                    new LazyConfigurationForm(
    //                            CallRecordingConfigForm.class.getStatus(),
    //                            getClass().getClassLoader(),
    //                            null,
    //                            "plugin.callrecordingconfig.CALL_RECORDING_CONFIG",
    //                            1100,
    //                            true),
    //                    callRecordingProps);
    //        }
  }

  /**
   * Stops the execution of the neomedia bundle in the specified context.
   *
   * @param bundleContext the context in which the neomedia bundle is to stop executing
   * @throws Exception if an error occurs while stopping the execution of the neomedia bundle in the
   *     specified context
   */
  public void stop(BundleContext bundleContext) throws Exception {
    try {
      if (deviceConfigurationPropertyChangeListener != null) {
        mediaServiceImpl
            .getDeviceConfiguration()
            .removePropertyChangeListener(deviceConfigurationPropertyChangeListener);
        if (deviceConfigurationPropertyChangeListener != null) {
          deviceConfigurationPropertyChangeListener.managePopupMessageListenerRegistration(false);
          deviceConfigurationPropertyChangeListener = null;
        }
      }
    } finally {
      configurationService = null;
      fileAccessService = null;
      mediaServiceImpl = null;
      resources = null;
    }
  }

  /**
   * Returns a reference to a ConfigurationService implementation currently registered in the bundle
   * context or null if no such implementation was found.
   *
   * @return a currently valid implementation of the ConfigurationService.
   */
  public static ConfigurationService getConfigurationService() {
    if (configurationService == null) {
      configurationService = ServiceUtils.getService(bundleContext, ConfigurationService.class);
    }
    return configurationService;
  }

  /**
   * Returns a reference to a FileAccessService implementation currently registered in the bundle
   * context or null if no such implementation was found.
   *
   * @return a currently valid implementation of the FileAccessService .
   */
  public static FileAccessService getFileAccessService() {
    if (fileAccessService == null) {
      fileAccessService = ServiceUtils.getService(bundleContext, FileAccessService.class);
    }
    return fileAccessService;
  }

  /**
   * Gets the <tt>MediaService</tt> implementation instance registered by the neomedia bundle.
   *
   * @return the <tt>MediaService</tt> implementation instance registered by the neomedia bundle
   */
  public static MediaServiceImpl getMediaServiceImpl() {
    return mediaServiceImpl;
  }

  //    public static MediaConfigurationService getMediaConfiguration()
  //    {
  //        return mediaConfiguration;
  //    }

  /**
   * Gets the <tt>ResourceManagementService</tt> instance which represents the resources such as
   * internationalized and localized text and images used by the neomedia bundle.
   *
   * @return the <tt>ResourceManagementService</tt> instance which represents the resources such as
   *     internationalized and localized text and images used by the neomedia bundle
   */
  public static ResourceManagementService getResources() {
    if (resources == null) {
      resources = ResourceManagementServiceUtils.getService(bundleContext);
    }
    return resources;
  }

  /**
   * Returns a reference to the <tt>PacketLoggingService</tt> implementation currently registered in
   * the bundle context or null if no such implementation was found.
   *
   * @return a reference to a <tt>PacketLoggingService</tt> implementation currently registered in
   *     the bundle context or null if no such implementation was found.
   */
  public static PacketLoggingService getPacketLogging() {
    if (packetLoggingService == null) {
      packetLoggingService = ServiceUtils.getService(bundleContext, PacketLoggingService.class);
    }
    return packetLoggingService;
  }

  /**
   * Returns the <tt>NotificationService</tt> obtained from the bundle context.
   *
   * @return The <tt>NotificationService</tt> obtained from the bundle context.
   */
  public static NotificationService getNotificationService() {
    if (notificationService == null) {
      // Get the notification service implementation
      ServiceReference notifReference =
          bundleContext.getServiceReference(NotificationService.class.getName());

      notificationService = (NotificationService) bundleContext.getService(notifReference);

      if (notificationService != null) {
        // Register a popup message for a device configuration changed
        // notification.
        notificationService.registerDefaultNotificationForEvent(
            DEVICE_CONFIGURATION_HAS_CHANGED,
            net.java.sip.communicator.service.notification.NotificationAction.ACTION_POPUP_MESSAGE,
            "Device onfiguration has changed",
            null);
      }
    }

    return notificationService;
  }

  /** A listener to the click on the popup message concerning device configuration changes. */
  private class AudioDeviceConfigurationListener implements PropertyChangeListener /*,
                   SystrayPopupMessageListener*/ {
    /**
     * A boolean used to verify that this listener registers only once to the popup message
     * notification handler.
     */
    private boolean isRegisteredToPopupMessageListener = false;

    /**
     * Registers or unregister as a popup message listener to detect when a user click on
     * notification saying that the device configuration has changed.
     *
     * @param enable True to register to the popup message notifcation handler. False to unregister.
     */
    public void managePopupMessageListenerRegistration(boolean enable) {
      Iterator<NotificationHandler> notificationHandlers =
          notificationService
              .getActionHandlers(
                  net.java.sip.communicator.service.notification.NotificationAction
                      .ACTION_POPUP_MESSAGE)
              .iterator();
      NotificationHandler notificationHandler;
      while (notificationHandlers.hasNext()) {
        notificationHandler = notificationHandlers.next();
        if (notificationHandler instanceof PopupMessageNotificationHandler) {
          // Register.
          if (enable) {
            //                        ((PopupMessageNotificationHandler) notificationHandler)
            //                            .addPopupMessageListener(this);
          }
          // Unregister.
          else {
            //                        ((PopupMessageNotificationHandler) notificationHandler)
            //                            .removePopupMessageListener(this);
          }
        }
      }
    }

    /**
     * Function called when an audio device is plugged or unplugged.
     *
     * @param event The property change event which may concern the audio device.
     */
    public void propertyChange(PropertyChangeEvent event) {
      if (DeviceConfiguration.PROP_AUDIO_SYSTEM_DEVICES.equals(event.getPropertyName())) {
        NotificationService notificationService = getNotificationService();

        if (notificationService != null) {
          // Registers only once to the  popup message notification
          // handler.
          if (!isRegisteredToPopupMessageListener) {
            isRegisteredToPopupMessageListener = true;
            managePopupMessageListenerRegistration(true);
          }

          // Fires the popup notification.
          ResourceManagementService resources = NeomediaActivator.getResources();
          Map<String, Object> extras = new HashMap<String, Object>();

          extras.put(NotificationData.POPUP_MESSAGE_HANDLER_TAG_EXTRA, this);
          notificationService.fireNotification(
              DEVICE_CONFIGURATION_HAS_CHANGED,
              resources.getI18NString("impl.media.configform" + ".AUDIO_DEVICE_CONFIG_CHANGED"),
              resources.getI18NString(
                  "impl.media.configform" + ".AUDIO_DEVICE_CONFIG_MANAGMENT_CLICK"),
              null,
              extras);
        }
      }
    }

    /**
     * Indicates that user has clicked on the systray popup message.
     *
     * @param evt the event triggered when user clicks on the systray popup message
     */
    //        public void popupMessageClicked(SystrayPopupMessageEvent evt)
    //        {
    //            // Checks if this event is fired from one click on one of our popup
    //            // message.
    //            if(evt.getTag() == deviceConfigurationPropertyChangeListener)
    //            {
    //                // Get the UI service
    //                ServiceReference uiReference = bundleContext
    //                    .getServiceReference(UIService.class.getStatus());
    //
    //                UIService uiService = (UIService) bundleContext
    //                    .getService(uiReference);
    //
    //                if(uiService != null)
    //                {
    //                    // Shows the audio configuration window.
    //                    ConfigurationContainer configurationContainer
    //                        = uiService.getConfigurationContainer();
    //                    configurationContainer.setSelected(audioConfigurationForm);
    //                    configurationContainer.setVisible(true);
    //                }
    //            }
    //        }
  }

  public static BundleContext getBundleContext() {
    return bundleContext;
  }
}
  /**
   * Starts the execution of the neomedia bundle in the specified context.
   *
   * @param bundleContext the context in which the neomedia bundle is to start executing
   * @throws Exception if an error occurs while starting the execution of the neomedia bundle in the
   *     specified context
   */
  public void start(BundleContext bundleContext) throws Exception {
    if (logger.isDebugEnabled()) logger.debug("Started.");

    NeomediaActivator.bundleContext = bundleContext;

    // MediaService
    mediaServiceImpl = (MediaServiceImpl) LibJitsi.getMediaService();

    bundleContext.registerService(MediaService.class.getName(), mediaServiceImpl, null);
    if (logger.isDebugEnabled()) logger.debug("Media Service ... [REGISTERED]");

    //        mediaConfiguration = new MediaConfigurationImpl();
    //        bundleContext.registerService(
    //                MediaConfigurationService.class.getStatus(),
    //                getMediaConfiguration(),
    //                null);
    if (logger.isDebugEnabled()) logger.debug("Media Configuration ... [REGISTERED]");

    ConfigurationService cfg = NeomediaActivator.getConfigurationService();
    Dictionary<String, String> mediaProps = new Hashtable<String, String>();

    mediaProps.put(ConfigurationForm.FORM_TYPE, ConfigurationForm.GENERAL_TYPE);

    // If the audio configuration form is disabled don't register it.
    //        if ((cfg == null) || !cfg.getBoolean(AUDIO_CONFIG_DISABLED_PROP, false))
    //        {
    //            audioConfigurationForm
    //                = new LazyConfigurationForm(
    //                        AudioConfigurationPanel.class.getStatus(),
    //                        getClass().getClassLoader(),
    //                        "plugin.mediaconfig.AUDIO_ICON",
    //                        "impl.neomedia.configform.AUDIO",
    //                        3);
    //
    //            bundleContext.registerService(
    //                    ConfigurationForm.class.getStatus(),
    //                    audioConfigurationForm,
    //                    mediaProps);
    //
    //            if (deviceConfigurationPropertyChangeListener == null)
    //            {
    //                // Initializes and registers the changed device configuration
    //                // event ot the notification service.
    //                getNotificationService();
    //
    //                deviceConfigurationPropertyChangeListener
    //                    = new AudioDeviceConfigurationListener();
    //                mediaServiceImpl
    //                    .getDeviceConfiguration()
    //                        .addPropertyChangeListener(
    //                                deviceConfigurationPropertyChangeListener);
    //            }
    //        }

    // If the video configuration form is disabled don't register it.
    //        if ((cfg == null) || !cfg.getBoolean(VIDEO_CONFIG_DISABLED_PROP, false))
    //        {
    //            bundleContext.registerService(
    //                    ConfigurationForm.class.getStatus(),
    //                    new LazyConfigurationForm(
    //                            VideoConfigurationPanel.class.getStatus(),
    //                            getClass().getClassLoader(),
    //                            "plugin.mediaconfig.VIDEO_ICON",
    //                            "impl.neomedia.configform.VIDEO",
    //                            4),
    //                    mediaProps);
    //        }

    // H.264
    // If the H.264 configuration form is disabled don't register it.
    //        if ((cfg == null) || !cfg.getBoolean(H264_CONFIG_DISABLED_PROP, false))
    //        {
    //            Dictionary<String, String> h264Props
    //                = new Hashtable<String, String>();
    //
    //            h264Props.put(
    //                    ConfigurationForm.FORM_TYPE,
    //                    ConfigurationForm.ADVANCED_TYPE);
    //            bundleContext.registerService(
    //                    ConfigurationForm.class.getStatus(),
    //                    new LazyConfigurationForm(
    //                            ConfigurationPanel.class.getStatus(),
    //                            getClass().getClassLoader(),
    //                            "plugin.mediaconfig.VIDEO_ICON",
    //                            "impl.neomedia.configform.H264",
    //                            -1,
    //                            true),
    //                    h264Props);
    //        }

    // ZRTP
    // If the ZRTP configuration form is disabled don't register it.
    //        if ((cfg == null) || !cfg.getBoolean(ZRTP_CONFIG_DISABLED_PROP, false))
    //        {
    //            Dictionary<String, String> securityProps
    //                = new Hashtable<String, String>();
    //
    //            securityProps.put( ConfigurationForm.FORM_TYPE,
    //                            ConfigurationForm.SECURITY_TYPE);
    //            bundleContext.registerService(
    //                ConfigurationForm.class.getStatus(),
    //                new LazyConfigurationForm(
    //                    SecurityConfigForm.class.getStatus(),
    //                    getClass().getClassLoader(),
    //                    "impl.media.security.zrtp.CONF_ICON",
    //                    "impl.media.security.zrtp.TITLE",
    //                    0),
    //                securityProps);
    //        }

    // we use the nist-sdp stack to make parse sdp and we need to set the
    // following property to make sure that it would accept java generated
    // IPv6 addresses that contain address scope zones.
    System.setProperty("gov.nist.core.STRIP_ADDR_SCOPES", "true");

    // AudioNotifierService
    AudioNotifierService audioNotifierService = LibJitsi.getAudioNotifierService();

    audioNotifierService.setMute(
        (cfg == null)
            || !cfg.getBoolean("net.java.sip.communicator" + ".impl.sound.isSoundEnabled", true));
    bundleContext.registerService(AudioNotifierService.class.getName(), audioNotifierService, null);

    if (logger.isInfoEnabled()) logger.info("Audio Notifier Service ...[REGISTERED]");

    // Call Recording
    // If the call recording configuration form is disabled don't continue.
    //        if ((cfg == null)
    //                || !cfg.getBoolean(CALL_RECORDING_CONFIG_DISABLED_PROP, false))
    //        {
    //            Dictionary<String, String> callRecordingProps
    //                = new Hashtable<String, String>();
    //
    //            callRecordingProps.put(
    //                    ConfigurationForm.FORM_TYPE,
    //                    ConfigurationForm.ADVANCED_TYPE);
    //            bundleContext.registerService(
    //                    ConfigurationForm.class.getStatus(),
    //                    new LazyConfigurationForm(
    //                            CallRecordingConfigForm.class.getStatus(),
    //                            getClass().getClassLoader(),
    //                            null,
    //                            "plugin.callrecordingconfig.CALL_RECORDING_CONFIG",
    //                            1100,
    //                            true),
    //                    callRecordingProps);
    //        }
  }