/****************************************************************************
* Copyright (C) 2012 ecsec GmbH.
* All rights reserved.
* Contact: ecsec GmbH (info@ecsec.de)
*
* This file is part of the Open eCard App.
*
* GNU General Public License Usage
* This file may be used under the terms of the GNU General Public
* License version 3.0 as published by the Free Software Foundation
* and appearing in the file LICENSE.GPL included in the packaging of
* this file. Please review the following information to ensure the
* GNU General Public License version 3.0 requirements will be met:
* http://www.gnu.org/copyleft/gpl.html.
*
* Other Usage
* Alternatively, this file may be used in accordance with the terms
* and conditions contained in a signed written agreement between
* you and ecsec GmbH.
*
***************************************************************************/
package org.openecard.richclient.gui;
import iso.std.iso_iec._24727.tech.schema.CardInfoType;
import iso.std.iso_iec._24727.tech.schema.CardTypeType;
import iso.std.iso_iec._24727.tech.schema.ConnectionHandleType;
import iso.std.iso_iec._24727.tech.schema.ConnectionHandleType.RecognitionInfo;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.InputStream;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.concurrent.ConcurrentSkipListMap;
import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.border.BevelBorder;
import oasis.names.tc.dss._1_0.core.schema.InternationalStringType;
import org.openecard.addon.AddonManager;
import org.openecard.common.I18n;
import org.openecard.common.enums.EventType;
import org.openecard.common.interfaces.EventCallback;
import org.openecard.gui.about.AboutDialog;
import org.openecard.recognition.CardRecognition;
import org.openecard.richclient.gui.manage.ManagementDialog;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* This class collects all events published by the EventManager in order to reproduce the current status including the
* connected card terminals and available cards. It is also responsible for creating the InfoPopup which displays the
* collected information.
*
* @author Johannes Schmölz <johannes.schmoelz@ecsec.de>
*/
public class Status implements EventCallback {
private static final Logger logger = LoggerFactory.getLogger(Status.class);
private static final String NO_TERMINAL_CONNECTED = "noTerminalConnected";
private final I18n lang = I18n.getTranslation("richclient");
private Map<String, JPanel> infoMap = new ConcurrentSkipListMap<String, JPanel>();
private HashMap<String, ImageIcon> cardIcons = new HashMap<String, ImageIcon>();
private JPanel contentPane;
private JPanel infoView;
private JPanel noTerminal;
private InfoPopup popup;
private final AppTray appTray;
private final CardRecognition recognition;
private final AddonManager manager;
/**
* Constructor of Status class.
*
* @param appTray tray icon
* @param recognition card recognition
* @param manager
*/
public Status(AppTray appTray, CardRecognition recognition, AddonManager manager) {
this.appTray = appTray;
this.recognition = recognition;
this.manager = manager;
setupBaseUI();
}
/**
* Shows the InfoPopup at the default position.
*/
public void showInfo() {
showInfo(null);
}
/**
* Shows the InfoPopup at the specified position p.
*
* @param p position
*/
public void showInfo(Point p) {
if (popup != null) {
popup.dispose();
}
popup = new InfoPopup(contentPane, p);
}
private void setupBaseUI() {
contentPane = new JPanel();
contentPane.setLayout(new BorderLayout());
contentPane.setBackground(Color.white);
contentPane.setBorder(BorderFactory.createBevelBorder(BevelBorder.RAISED, Color.LIGHT_GRAY, Color.DARK_GRAY));
noTerminal = new JPanel();
noTerminal.setLayout(new FlowLayout(FlowLayout.LEFT));
noTerminal.setBackground(Color.white);
noTerminal.add(createInfoLabel());
infoMap.put(NO_TERMINAL_CONNECTED, noTerminal);
infoView = new JPanel();
infoView.setLayout(new BoxLayout(infoView, BoxLayout.PAGE_AXIS));
infoView.setBackground(Color.white);
infoView.add(Box.createRigidArea(new Dimension(0, 5)));
infoView.add(noTerminal);
JLabel label = new JLabel(" " + lang.translationForKey("tray.title") + " ");
label.setFont(new Font(Font.SANS_SERIF, Font.BOLD, 16));
GradientPanel panel = new GradientPanel(new Color(106, 163, 213), new Color(80, 118, 177));
panel.setOpaque(false);
panel.add(label);
JPanel btnPanel = new JPanel();
btnPanel.setLayout(new FlowLayout(FlowLayout.RIGHT));
btnPanel.setBackground(Color.white);
JButton btnExit = new JButton(lang.translationForKey("tray.exit"));
btnExit.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
appTray.shutdown();
}
});
JButton btnAbout = new JButton(lang.translationForKey("tray.about"));
btnAbout.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
AboutDialog.showDialog();
}
});
JButton btnSettings = new JButton(lang.translationForKey("tray.config"));
btnSettings.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
ManagementDialog.showDialog(manager);
}
});
btnPanel.add(btnSettings);
btnPanel.add(btnAbout);
btnPanel.add(btnExit);
contentPane.add(panel, BorderLayout.NORTH);
contentPane.add(infoView, BorderLayout.CENTER);
contentPane.add(btnPanel, BorderLayout.SOUTH);
}
private synchronized void addInfo(String ifdName, RecognitionInfo info) {
if (infoMap.containsKey(NO_TERMINAL_CONNECTED)) {
infoMap.remove(NO_TERMINAL_CONNECTED);
infoView.removeAll();
}
JPanel panel = new JPanel();
panel.setLayout(new FlowLayout(FlowLayout.LEFT));
panel.add(createInfoLabel(ifdName, info));
infoMap.put(ifdName, panel);
infoView.add(panel);
if (popup != null) {
popup.updateContent(contentPane);
}
}
private synchronized void updateInfo(String ifdName, RecognitionInfo info) {
JPanel panel = infoMap.get(ifdName);
if (panel != null) {
panel.removeAll();
panel.add(createInfoLabel(ifdName, info));
if (popup != null) {
popup.updateContent(contentPane);
}
}
}
private synchronized void removeInfo(String ifdName) {
JPanel panel = infoMap.get(ifdName);
if (panel != null) {
infoMap.remove(ifdName);
infoView.remove(panel);
if (infoMap.isEmpty()) {
infoMap.put(NO_TERMINAL_CONNECTED, noTerminal);
infoView.add(noTerminal);
}
if (popup != null) {
popup.updateContent(contentPane);
}
}
}
private synchronized ImageIcon getCardIcon(String cardType) {
if (cardType == null) {
cardType = "http://openecard.org/cif/no-card";
}
if (! cardIcons.containsKey(cardType)) {
InputStream is = recognition.getCardImage(cardType);
if (is == null) {
is = recognition.getUnknownCardImage();
}
ImageIcon icon = GuiUtils.getImageIcon(is);
cardIcons.put(cardType, icon);
}
return cardIcons.get(cardType);
}
private String getCardType(RecognitionInfo info) {
if (info != null) {
String cardType = info.getCardType();
if (cardType != null) {
return resolveCardType(cardType);
} else {
return lang.translationForKey("status.nocard");
}
} else {
return lang.translationForKey("status.nocard");
}
}
private String resolveCardType(String cardType) {
if (cardType.equals("http://bsi.bund.de/cif/unknown")) {
return lang.translationForKey("status.unknowncard");
} else {
// read CardTypeName from CardInfo file
CardInfoType cif = recognition.getCardInfo(cardType);
String cardTypeName = cardType;
if (cif != null) {
CardTypeType type = cif.getCardType();
if (type != null) {
boolean found = false;
String[] languages = new String[] {Locale.getDefault().getLanguage(), "en"};
// check native lang, then english
for (String language : languages) {
if (found) { // stop when the inner loop terminated
break;
}
List<InternationalStringType> cardTypeNames = type.getCardTypeName();
for (InternationalStringType ist : cardTypeNames) {
if (ist.getLang().equalsIgnoreCase(language)) {
cardTypeName = ist.getValue();
found = true;
break;
}
}
}
}
}
return cardTypeName;
}
}
private JLabel createInfoLabel() {
return createInfoLabel(null, null);
}
private JLabel createInfoLabel(String ifdName, RecognitionInfo info) {
JLabel label = new JLabel();
if (ifdName != null) {
String cardType = info != null ? info.getCardType() : "http://openecard.org/cif/no-card";
label.setIcon(getCardIcon(cardType));
label.setText("<html><b>" + getCardType(info) + "</b><br><i>" + ifdName + "</i></html>");
} else {
// no_terminal.png is based on klaasvangend_USB_plug.svg by klaasvangend
// see: http://openclipart.org/detail/3705/usb-plug-by-klaasvangend
label.setIcon(getCardIcon("http://openecard.org/cif/no-terminal"));
label.setText("<html><i>" + lang.translationForKey("status.noterminal") + "</i></html>");
}
label.setIconTextGap(10);
label.setBackground(Color.white);
// on Windows the label width is too small to display all information
Dimension dim = label.getPreferredSize();
label.setPreferredSize(new Dimension(dim.width + 10, dim.height));
return label;
}
@Override
public void signalEvent(EventType eventType, Object eventData) {
logger.debug("Event: {}", eventType);
if (eventData instanceof ConnectionHandleType) {
ConnectionHandleType ch = (ConnectionHandleType) eventData;
logger.debug("ConnectionHandle: {}", ch);
RecognitionInfo info = ch.getRecognitionInfo();
logger.debug("RecognitionInfo: {}", info);
String ifdName = ch.getIFDName();
logger.debug("IFDName: {}", ifdName);
if (eventType.equals(EventType.TERMINAL_ADDED)) {
addInfo(ifdName, info);
} else if (eventType.equals(EventType.TERMINAL_REMOVED)) {
removeInfo(ifdName);
} else {
updateInfo(ifdName, info);
}
}
}
}