/*
* Jitsi, the OpenSource Java VoIP and Instant Messaging client.
*
* Copyright @ 2015 Atlassian Pty Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.java.sip.communicator.impl.gui.main.call;
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import java.util.List;
import javax.swing.*;
import net.java.sip.communicator.impl.gui.*;
import net.java.sip.communicator.impl.gui.main.account.*;
import net.java.sip.communicator.impl.gui.utils.*;
import net.java.sip.communicator.plugin.desktoputil.*;
import net.java.sip.communicator.util.skin.*;
import org.jitsi.service.neomedia.*;
import org.jitsi.service.neomedia.device.*;
import org.jitsi.util.*;
import org.jitsi.util.swing.TransparentPanel;
import com.explodingpixels.macwidgets.*;
/**
* The <tt>PreCallDialog</tt> is a dialog allowing to pick-up or hangup a call.
* This is the parent dialog of the <tt>ReceivedCallDialog</tt> and the
* <tt>ChooseCallAccountDialog</tt>.
*
* @author Yana Stamcheva
*/
public abstract class PreCallDialog
implements ActionListener,
Skinnable
{
/**
* List of PreCallDialog windows that are currently visible to the user.
*/
private static final List<Window> visibleWindows = new ArrayList<Window>();
/**
* The call button name.
*/
private static final String CALL_BUTTON = "CallButton";
/**
* The conference call button name.
*/
private static final String CONF_CALL_BUTTON = "ConfCallButton";
/**
* The call button name.
*/
private static final String VIDEO_CALL_BUTTON = "VideoCallButton";
/**
* The hangup button name.
*/
private static final String HANGUP_BUTTON = "HangupButton";
/**
* Call button.
*/
private SIPCommButton callButton;
/**
* Conference call button to answer the call in an existing call or
* conference.
*/
private SIPCommButton mergeCallButton;
/**
* Video call button.
*/
private SIPCommButton videoCallButton;
/**
* HandUp button.
*/
private SIPCommButton hangupButton;
/**
* Call label for display name.
*/
private JLabel callLabelDisplayName;
/**
* Call label for address.
*/
private JLabel callLabelAddress;
/**
* Call label for account.
*/
private JLabel callLabelAccount;
/**
* The label that will contain the peer image.
*/
private JLabel callLabelImage;
/**
* The combo box containing a list of accounts to choose from.
*/
private JComboBox accountsCombo;
/**
* The window handling received calls.
*/
private final Window preCallWindow;
/**
* If it is a video call.
*/
private final boolean video;
/**
* If the call should be answered in an existing call.
*/
private final boolean mergeCall;
/**
* Creates an instance of <tt>PreCallDialog</tt> by specifying the dialog
* title.
*
* @param title the title of the dialog
* @param video if it is a video call
* @param existingCall true to answer call in an existing call
*/
public PreCallDialog(String title, boolean video, boolean existingCall)
{
this(title, null, null, video, existingCall);
}
/**
* Creates an instance of <tt>PreCallDialog</tt> by specifying the dialog
* title and the text to show.
*
* @param title the title of the dialog
* @param text the text to show
* @param accounts the list of accounts to choose from
*/
public PreCallDialog(String title, String text, Account[] accounts)
{
this(title, text, accounts, false, false);
}
/**
* Creates an instance of <tt>PreCallDialog</tt> by specifying the dialog
* title and the text to show.
*
* @param title the title of the dialog
* @param text the text to show
* @param accounts the list of accounts to choose from
* @param video if it is a video call
* @param existingCall true to answer call in an existing call
*/
public PreCallDialog(String title, String text, Account[] accounts,
boolean video, boolean existingCall)
{
preCallWindow = createPreCallWindow(title, text, accounts);
if (video)
{
// Make sure there is a VIDEO MediaDevice and it is capable of
// capture/sending
MediaDevice mediaDevice
= GuiActivator.getMediaService().getDefaultDevice(
MediaType.VIDEO,
MediaUseCase.CALL);
if ((mediaDevice == null)
|| !mediaDevice.getDirection().allowsSending())
video = false;
}
this.video = video;
this.mergeCall = existingCall;
initComponents();
}
/**
* Creates this received call window.
*
* @param title the title of the created window
* @param text the text to show
* @param accounts the list of accounts to choose from
* @return the created window
*/
private Window createPreCallWindow( String title,
String text,
Account[] accounts)
{
Window receivedCallWindow = null;
if (OSUtils.IS_MAC)
{
HudWindow window = new HudWindow();
window.hideCloseButton();
JDialog dialog = window.getJDialog();
dialog.setUndecorated(true);
dialog.setTitle(title);
receivedCallWindow = window.getJDialog();
callLabelDisplayName = HudWidgetFactory.createHudLabel("");
callLabelAddress = HudWidgetFactory.createHudLabel("");
callLabelImage = HudWidgetFactory.createHudLabel("");
if (accounts != null)
{
accountsCombo
= HudWidgetFactory.createHudComboBox(
new DefaultComboBoxModel(accounts));
}
else
callLabelAccount = HudWidgetFactory.createHudLabel("");
}
else
{
SIPCommFrame frame = new SIPCommFrame(false);
frame.setUndecorated(true);
receivedCallWindow = frame;
callLabelDisplayName = new JLabel();
callLabelAddress = new JLabel();
callLabelImage = new JLabel();
if (accounts != null)
accountsCombo = new JComboBox(accounts);
else
callLabelAccount = new JLabel();
}
if (text != null)
callLabelDisplayName.setText(text);
receivedCallWindow.setAlwaysOnTop(true);
// Register the JFrame so the dialog window is able to get dragged
// across the screen
ComponentMover.registerComponent(receivedCallWindow);
// prevents dialog window to get unwanted key events and when going
// on top on linux, it steals focus and if we are accidentally
// writing something and pressing enter a call get answered
receivedCallWindow.setFocusableWindowState(false);
return receivedCallWindow;
}
/**
* Initializes all components in this panel.
*/
private void initComponents()
{
JPanel mainPanel = new TransparentPanel(new BorderLayout(10, 0));
mainPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
// disable html rendering
callLabelDisplayName.putClientProperty("html.disable", Boolean.TRUE);
callLabelAddress.putClientProperty("html.disable", Boolean.TRUE);
callLabelImage.putClientProperty("html.disable", Boolean.TRUE);
if(callLabelAccount != null)
callLabelAccount.putClientProperty("html.disable", Boolean.TRUE);
JComponent buttonsPanel = new CallToolBar(false, true);
callButton = new SIPCommButton();
if(mergeCall)
mergeCallButton = new SIPCommButton();
if(video)
videoCallButton = new SIPCommButton();
hangupButton = new SIPCommButton();
callButton.setName(CALL_BUTTON);
hangupButton.setName(HANGUP_BUTTON);
callButton.addActionListener(this);
if(mergeCall)
{
mergeCallButton.setName(CONF_CALL_BUTTON);
mergeCallButton.addActionListener(this);
}
hangupButton.addActionListener(this);
if(video)
{
videoCallButton.setName(VIDEO_CALL_BUTTON);
videoCallButton.addActionListener(this);
}
preCallWindow.add(mainPanel);
mainPanel.add(callLabelImage, BorderLayout.WEST);
JPanel labelsPanel = new TransparentPanel();
labelsPanel.setBorder(BorderFactory.createEmptyBorder(10, 0, 10, 0));
labelsPanel.setLayout(new BoxLayout(labelsPanel, BoxLayout.Y_AXIS));
callLabelDisplayName.setAlignmentX(JLabel.LEFT_ALIGNMENT);
labelsPanel.add(callLabelDisplayName);
labelsPanel.add(Box.createVerticalStrut(3));
callLabelAddress.setAlignmentX(JLabel.LEFT_ALIGNMENT);
labelsPanel.add(callLabelAddress);
if(callLabelAccount != null)
{
labelsPanel.add(Box.createVerticalStrut(3));
callLabelAccount.setAlignmentX(JLabel.LEFT_ALIGNMENT);
labelsPanel.add(callLabelAccount);
}
if (accountsCombo != null)
{
labelsPanel.add(Box.createVerticalStrut(3));
accountsCombo.setAlignmentX(JLabel.LEFT_ALIGNMENT);
labelsPanel.add(accountsCombo);
}
mainPanel.add(labelsPanel, BorderLayout.CENTER);
// Loads skin resources.
loadSkin();
JPanel rightPanel = new TransparentPanel();
rightPanel.setLayout(new BoxLayout(rightPanel, BoxLayout.Y_AXIS));
rightPanel.setBorder(BorderFactory.createEmptyBorder(6, 0, 0, 0));
buttonsPanel.add(callButton);
if(mergeCall)
buttonsPanel.add(mergeCallButton);
if(video)
buttonsPanel.add(videoCallButton);
buttonsPanel.add(hangupButton);
buttonsPanel.setAlignmentX(Component.CENTER_ALIGNMENT);
rightPanel.add(Box.createVerticalGlue());
rightPanel.add(buttonsPanel);
rightPanel.add(Box.createVerticalGlue());
mainPanel.add(rightPanel, BorderLayout.EAST);
}
/**
* Packs the content of this dialog.
*/
public void pack()
{
preCallWindow.pack();
}
/**
* Shows/hides this dialog.
*
* Synchronized to ensure that only one window can update the global
* visibleWindows datastructure at a time.
*
* @param isVisible indicates if this dialog should be shown or hidden
*/
public synchronized void setVisible(boolean isVisible)
{
if (isVisible)
{
preCallWindow.pack();
// Set the visibility before setting the location. This is the only
// reliable way of ensure that the location is respected.
preCallWindow.setVisible(isVisible);
if (visibleWindows.isEmpty())
{
// No existing visible windows displayed. Just center this one.
preCallWindow.setLocationRelativeTo(null);
}
else
{
// Get the location of last displayed window and and the
// height of it, so the this window appears directly underneath.
Window last = visibleWindows.get(visibleWindows.size() - 1);
int newX = last.getX();
int newY = last.getY() + last.getHeight();
preCallWindow.setLocation(newX, newY);
}
visibleWindows.add(preCallWindow);
}
else
{
// Remove the window from the list of visible windows. This also
// occurs in dispose()
visibleWindows.remove(preCallWindow);
preCallWindow.setVisible(isVisible);
}
}
/**
* Indicates if this dialog is currently visible.
*
* @return <tt>true</tt> if this dialog is currently visible, <tt>false</tt>
* otherwise
*/
public boolean isVisible()
{
return preCallWindow.isVisible();
}
/**
* Disposes this window.
*/
public void dispose()
{
visibleWindows.remove(preCallWindow);
preCallWindow.dispose();
}
/**
* Returns the labels contained in this dialog.
* The first is the label that will contain the image.
* The second one is the one with the display name(s).
* The third is the one that will hold the peer address(s).
*
* @return the call label contained in this dialog
*/
public JLabel[] getCallLabels()
{
if(callLabelAccount == null)
return new JLabel[]{
callLabelImage, callLabelDisplayName, callLabelAddress};
else
return new JLabel[]{
callLabelImage,
callLabelDisplayName,
callLabelAddress,
callLabelAccount};
}
/**
* Returns the accounts combo box contained in this dialog.
*
* @return the accounts combo box contained in this dialog
*/
public JComboBox getAccountsCombo()
{
return accountsCombo;
}
/**
* Reloads icons.
*/
public void loadSkin()
{
callButton.setBackgroundImage(
ImageLoader.getImage(ImageLoader.INCOMING_CALL_BUTTON_BG));
callButton.setRolloverImage(
ImageLoader.getImage(ImageLoader.INCOMING_CALL_BUTTON_ROLLOVER));
callButton.setPressedImage(
ImageLoader.getImage(ImageLoader.INCOMING_CALL_BUTTON_PRESSED));
if (videoCallButton != null)
{
videoCallButton.setBackgroundImage(
ImageLoader.getImage(ImageLoader.CALL_VIDEO_BUTTON_BG));
videoCallButton.setRolloverImage(
ImageLoader.getImage(ImageLoader.CALL_VIDEO_BUTTON_ROLLOVER));
videoCallButton.setPressedImage(
ImageLoader.getImage(ImageLoader.CALL_VIDEO_BUTTON_PRESSED));
}
if (mergeCallButton != null)
{
mergeCallButton.setBackgroundImage(
ImageLoader.getImage(ImageLoader.MERGE_CALL_BUTTON_BG));
mergeCallButton.setRolloverImage(
ImageLoader.getImage(ImageLoader.MERGE_CALL_BUTTON_ROLLOVER));
mergeCallButton.setPressedImage(
ImageLoader.getImage(ImageLoader.MERGE_CALL_BUTTON_PRESSED));
}
hangupButton.setBackgroundImage(
ImageLoader.getImage(ImageLoader.HANGUP_BUTTON_BG));
hangupButton.setRolloverImage(
ImageLoader.getImage(ImageLoader.HANGUP_BUTTON_ROLLOVER));
hangupButton.setPressedImage(
ImageLoader.getImage(ImageLoader.HANGUP_BUTTON_PRESSED));
}
/**
* Handles <tt>ActionEvent</tt>s triggered by pressing the call or the
* hangup buttons.
* @param e The <tt>ActionEvent</tt> to handle.
*/
public void actionPerformed(ActionEvent e)
{
JButton button = (JButton) e.getSource();
String buttonName = button.getName();
if (buttonName.equals(CALL_BUTTON))
{
callButtonPressed();
}
else if (buttonName.equals(CONF_CALL_BUTTON))
{
mergeCallButtonPressed();
}
else if (buttonName.equals(VIDEO_CALL_BUTTON))
{
videoCallButtonPressed();
}
else if (buttonName.equals(HANGUP_BUTTON))
{
hangupButtonPressed();
}
dispose();
}
/**
* Indicates that the call button has been pressed.
*/
public abstract void callButtonPressed();
/**
* Indicates that the conference call button has been pressed.
*/
public abstract void mergeCallButtonPressed();
/**
* Indicates that the hangup button has been pressed.
*/
public abstract void hangupButtonPressed();
/**
* Indicates that the video call button has been pressed.
*/
public abstract void videoCallButtonPressed();
}