package ru.denull.wire;
import static ru.denull.mtproto.CryptoUtils.*;
import java.awt.*;
import java.awt.Cursor;
import java.awt.Dialog;
import javax.imageio.ImageIO;
import javax.swing.*;
import java.awt.BorderLayout;
import javax.swing.*;
import java.awt.FlowLayout;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.awt.dnd.*;
import java.awt.event.*;
import java.io.*;
import java.lang.reflect.InvocationTargetException;
import java.math.BigInteger;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.security.NoSuchAlgorithmException;
import java.util.*;
import java.util.prefs.BackingStoreException;
import java.util.prefs.Preferences;
import javax.swing.*;
import javax.swing.border.EmptyBorder;
import javax.swing.event.*;
import javax.swing.AbstractListModel;
import javax.swing.JButton;
import javax.swing.JOptionPane;
//import com.apple.eawt.Application;
import ru.denull.mtproto.DataService;
import ru.denull.mtproto.DataService.OnUpdateListener;
import ru.denull.mtproto.Server;
import ru.denull.mtproto.Auth.AuthCallback;
import ru.denull.mtproto.Server.RPCCallback;
import ru.denull.wire.model.*;
import ru.denull.wire.model.Config;
import ru.denull.wire.model.DialogManager.EncryptedDialog;
import ru.denull.wire.model.FileManager.FileUploadingProgressiveCallback;
import ru.denull.wire.model.TypingManager.TypingCallback;
import sun.misc.IOUtils;
import tl.*;
import tl.Chat;
import tl.ChatFull;
import tl.Message;
import tl.TChat;
import tl.TMessage;
import tl.contacts.*;
import tl.messages.*;
public class Main implements OnUpdateListener, TypingCallback {
private JFrame frame;
private JList dialogList, messageList;
private JTextField searchField;
private TInputPeer currentPeer;
private int currentEncryptedChat = -1;
private ChatFull currentChat;
public static DataService service;
public static Main window;
private MessageListModel messageListModel;
private DialogCellRenderer dialogListRenderer;
static public ContactListModel contactListModel;
private ContactListRenderer contactListRenderer;
private JTextField messageField;
private JToggleButton dialogsBtn;
private JToggleButton contactsBtn;
final JFileChooser fc = new JFileChooser();
FileDialog fd, importDialog;
public static FileDialog saveDialog;
private JPanel sendPanel;
public static int currentMods = 0;
private JButton actionBtn;
private JPanel chatPanel;
private JLabel titleLabel;
private JLabel titleStatus;
private JButton titleInfoBtn;
private JButton titleActionBtn;
/**
* Launch the application.
*/
public static void main(String[] args) {
//Application app = Application.getApplication();
//app.setDockIconImage( Utils.getImage("icon_128x128.png"));
System.setProperty("apple.laf.useScreenMenuBar", "true");
System.setProperty("com.apple.mrj.application.apple.menu.about.name", "Wire");
if (System.getProperty("os.name").contains("Mac")) {
try {
Object app =
Class.forName("com.apple.eawt.Application")
.getMethod("getApplication", (Class[]) null)
.invoke(null, (Object[]) null);
app.getClass()
.getMethod("setDockIconImage", new Class[] { Image.class })
.invoke(app, new Object[] { Utils.getImage("icon_128x128.png") });
} catch (Exception e) {
//fail quietly
}
} else {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (Exception e) {
e.printStackTrace();
}
}
System.setProperty("awt.useSystemAAFontSettings","on");
System.setProperty("swing.aatext", "true");
//UIManager.put("List.lockToPositionOnScroll", Boolean.FALSE);
Font font = new Font(Utils.fontName, java.awt.Font.PLAIN, 12);
Enumeration keys = UIManager.getDefaults().keys();
while (keys.hasMoreElements()) {
Object key = keys.nextElement();
Object value = UIManager.get(key);
if (value instanceof Font) {
UIManager.put(key, font);
}
}
//SwingUtilities.updateComponentTreeUI(frame);
service = DataService.getInstance();
service.connectAndPrepare(false, false, new AuthCallback() {
public void error() {
System.out.println("Unable to generate auth key");
}
public void done(Server server, byte[] auth_key) {
System.out.println("Auth key generated");
Notifier.enter(Notifier.MAIN_SERVER_AUTH_KEY);
/*ui(new Runnable() {
public void run() {
if (chatlist != null && chatlist.getListAdapter() != null) {
((ChatListAdapter) chatlist.getListAdapter()).preload(true);
}
}
}, true);*/
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
window = new Main();
window.frame.setVisible(true);
service.updateListener = window;
//service.me = null;
if (service.me != null) {
service.dialogManager.reloadDialogs();
window.contactListModel.reload();
} else {
AuthDialog authDialog = new AuthDialog(window.frame, Dialog.ModalityType.DOCUMENT_MODAL);
authDialog.setVisible(true);
}
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
});
/*try {
UIManager.setLookAndFeel(UIManager.getCrossPlatformLookAndFeelClassName());
} catch (Exception e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}*/
}
/**
* Create the application.
*/
public Main() {
initialize();
}
public void setMultipleMode() {
/*try {
fd.setMultipleMode(true);
} catch (Exception e) {
e.printStackTrace();
}*/
}
/**
* Initialize the contents of the frame.
*/
private void initialize() {
frame = new JFrame();
Dimension dim = Toolkit.getDefaultToolkit().getScreenSize();
frame.setBounds((dim.width - 1100) / 2, (dim.height - 660) / 2, 1100, 660);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().setLayout(new BorderLayout(0, 0));
frame.setTitle("Wire");
fd = new FileDialog(frame, "Выберите изображения или видеозаписи для загрузки", FileDialog.LOAD);
importDialog = new FileDialog(frame, "Выберите список контактов для импорта", FileDialog.LOAD);
saveDialog = new FileDialog(frame, "Сохранить как...", FileDialog.SAVE);
/*try {
fd.getClass().getMethod("setMultipleMode", new Class[] { Boolean.class } ).invoke(fd, true);
} catch (Exception e1) {
e1.printStackTrace();
}*/
setMultipleMode();
fd.setFilenameFilter(new FilenameFilter() {
public boolean accept(File dir, String name) {
name = name.toLowerCase();
return name.endsWith(".jpg") || name.endsWith(".jpeg") || name.endsWith(".png") || name.endsWith(".gif") || name.endsWith(".avi") || name.endsWith(".mov") || name.endsWith(".mp4") || name.endsWith(".webp") || name.endsWith(".mp3");
}
});
importDialog.setFilenameFilter(new FilenameFilter() {
public boolean accept(File dir, String name) {
name = name.toLowerCase();
return name.endsWith(".csv") || name.endsWith(".vcf");
}
});
JMenuBar menuBar = new JMenuBar();
JMenu menu;
JMenuItem menuItem;
menu = new JMenu("Настройки");
menuBar.add(menu);
menuItem = new JMenuItem("Сбросить авторизацию и выйти");
menuItem.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
Preferences pref = Preferences.userRoot().node("wire");
try {
pref.removeNode();
} catch (BackingStoreException e1) {
e1.printStackTrace();
}
System.out.println("Removed all preferences, exiting");
System.exit(0);
}
});
menu.add(menuItem);
menu = new JMenu("Контакты");
menuBar.add(menu);
menuItem = new JMenuItem("Импорт...");
menuItem.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
importContacts();
}
});
menu.add(menuItem);
menuItem = new JMenuItem("Импорт с заменой...");
menuItem.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
importContacts(true);
}
});
menu.add(menuItem);
menuItem = new JMenuItem("Добавить контакт...");
menuItem.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
addContact();
}
});
menu.add(menuItem);
frame.setJMenuBar(menuBar);
JSplitPane splitPane = new JSplitPane();
splitPane.setContinuousLayout(true);
splitPane.setDividerSize(1);
splitPane.setBackground(Color.decode("0xe0e0e0"));
splitPane.setBorder(new EmptyBorder(0, 0, 0, 0));
splitPane.setDividerLocation(320);
frame.getContentPane().add(splitPane, BorderLayout.CENTER);
JPanel listPanel = new JPanel();
splitPane.setLeftComponent(listPanel);
listPanel.setLayout(new BorderLayout(0, 0));
JPanel searchPanel = new JPanel();
listPanel.add(searchPanel, BorderLayout.NORTH);
searchPanel.setBackground(Color.decode("0xf9f9f9"));
searchPanel.setLayout(new BoxLayout(searchPanel, BoxLayout.X_AXIS));
searchPanel.add(Box.createRigidArea(new Dimension(6, 40)));
searchField = new JTextField();
searchField.putClientProperty("JTextField.variant", "search");
//searchField.putClientProperty("JTextField.Search.Prompt", "Найти...");
searchField.setAlignmentY(Component.CENTER_ALIGNMENT);
searchField.setPreferredSize(new Dimension(0, 26));
searchField.setMaximumSize(new Dimension(Integer.MAX_VALUE, 26));
searchField.getDocument().addDocumentListener(new DocumentListener() {
public void removeUpdate(DocumentEvent e) {
String query = searchField.getForeground().equals(Color.LIGHT_GRAY) ? null : searchField.getText();
service.dialogManager.filter(query);
contactListModel.filter(query);
restoreDialogSelection();
}
public void insertUpdate(DocumentEvent e) {
String query = searchField.getForeground().equals(Color.LIGHT_GRAY) ? null : searchField.getText();
service.dialogManager.filter(query);
contactListModel.filter(query);
restoreDialogSelection();
}
public void changedUpdate(DocumentEvent e) {
String query = searchField.getForeground().equals(Color.LIGHT_GRAY) ? null : searchField.getText();
service.dialogManager.filter(query);
contactListModel.filter(query);
restoreDialogSelection();
}
});
searchPanel.add(searchField);
searchField.setColumns(10);
searchPanel.add(Box.createRigidArea(new Dimension(3, 0)));
dialogsBtn = new JToggleButton("");
dialogsBtn.putClientProperty("JButton.buttonType", "segmentedCapsule");
dialogsBtn.putClientProperty("JButton.segmentPosition", "first");
dialogsBtn.setFocusable(false);
dialogsBtn.setSelected(true);
dialogsBtn.setPreferredSize(new Dimension(32, 26));
dialogsBtn.setMinimumSize(new Dimension(32, 26));
dialogsBtn.setMaximumSize(new Dimension(32, 26));
//dialogsBtn.setIcon(new ImageIcon(Utils.getImage("dialogs_up.png")));
//dialogsBtn.setSelectedIcon(new ImageIcon(Utils.getImage("dialogs_down.png")));
dialogsBtn.setIcon(new ImageIcon(Utils.getImage("chats.png")));
dialogsBtn.setSelectedIcon(new ImageIcon(Utils.getImage("chats_selected.png")));
//dialogsBtn.setPressedIcon(new ImageIcon(Utils.getImage("dialogs_down.png")));
dialogsBtn.setIconTextGap(0);
dialogsBtn.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
dialogsBtn.setSelected(true);
contactsBtn.setSelected(false);
actionBtn.setText("Групповой чат...");
dialogList.setCellRenderer(null);
dialogList.setFixedCellHeight(66);
dialogList.setModel(service.dialogManager);
dialogList.setCellRenderer(dialogListRenderer);
restoreDialogSelection();
}
});
searchPanel.add(dialogsBtn);
contactsBtn = new JToggleButton("");
contactsBtn.putClientProperty("JButton.buttonType", "segmentedCapsule");
contactsBtn.putClientProperty("JButton.segmentPosition", "last");
contactsBtn.setFocusable(false);
contactsBtn.setPreferredSize(new Dimension(32, 26));
contactsBtn.setMinimumSize(new Dimension(32, 26));
contactsBtn.setMaximumSize(new Dimension(32, 26));
//contactsBtn.setIcon(new ImageIcon(Utils.getImage("contacts_up.png")));
//contactsBtn.setSelectedIcon(new ImageIcon(Utils.getImage("contacts_down.png")));
contactsBtn.setIcon(new ImageIcon(Utils.getImage("contacts.png")));
contactsBtn.setSelectedIcon(new ImageIcon(Utils.getImage("contacts_selected.png")));
//contactsBtn.setPressedIcon(new ImageIcon(Utils.getImage("contacts_down.png")));
contactsBtn.setIconTextGap(0);
contactsBtn.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
dialogsBtn.setSelected(false);
contactsBtn.setSelected(true);
actionBtn.setText("Добавить контакт...");
dialogList.setCellRenderer(null);
dialogList.setFixedCellHeight(-1);
dialogList.setModel(contactListModel);
dialogList.setCellRenderer(contactListRenderer);
restoreDialogSelection();
}
});
searchPanel.add(contactsBtn);
searchPanel.add(Box.createRigidArea(new Dimension(6, 0)));
dialogList = new InteractiveList() {
public boolean getScrollableTracksViewportWidth() {
return true;
}
};
dialogList.setBackground(Color.WHITE);
//dialogList.setBorder(UIManager.getBorder("List.sourceListBackgroundPainter"));
dialogListRenderer = new DialogCellRenderer(service);
dialogList.setCellRenderer(dialogListRenderer);
dialogList.setFixedCellHeight(66);
dialogList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
dialogList.addListSelectionListener(new ListSelectionListener() {
public void valueChanged(ListSelectionEvent e) {
if (dialogList.getSelectedIndex() > -1) {
if (chatPanel.isVisible()) {
/*if (contactListModel.isEmpty()) return;
chatMemberModel.add((Integer) contactListModel.getElementAt(dialogList.getSelectedIndex()), true);
chatActionBtn.setEnabled(!chatTitleField.getForeground().equals(Color.LIGHT_GRAY) && !chatTitleField.getText().isEmpty() && !chatMemberModel.isEmpty());
//dialogList.clearSelection();*/
} else
if (dialogsBtn.isSelected()) {
if (service.dialogManager.isEmpty()) return;
tl.Dialog dialog = (tl.Dialog) service.dialogManager.getElementAt(dialogList.getSelectedIndex());
selectDialog(dialog);
} else
if (contactsBtn.isSelected()) {
if (contactListModel.isEmpty()) return;
TUser user = service.userManager.get((Integer) contactListModel.getElementAt(dialogList.getSelectedIndex()));
selectDialog(user, null, null);
}
}
}
});
dialogList.setModel(service.dialogManager);
JScrollPane scrollPane = new JScrollPane(dialogList);
scrollPane.setBackground(Color.decode("0xf9f9f9"));
scrollPane.setBorder(BorderFactory.createMatteBorder(1, 0, 1, 0, Color.decode("0xe0e0e0")));
scrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
listPanel.add(scrollPane, BorderLayout.CENTER);
contactListModel = new ContactListModel(service, dialogList);
contactListRenderer = new ContactListRenderer(service);
JPanel actionPanel = new JPanel();
//actionPanel.setBorder(BorderFactory.createMatteBorder(1, 0, 0, 0, Color.decode("0xe0e0e0")));
actionPanel.setBackground(Color.decode("0xf9f9f9"));
actionPanel.setLayout(new BoxLayout(actionPanel, BoxLayout.X_AXIS));
listPanel.add(actionPanel, BorderLayout.SOUTH);
actionPanel.add(Box.createRigidArea(new Dimension(6, 40)));
actionBtn = new JButton("Групповой чат...");
actionBtn.setMaximumSize(new Dimension(Integer.MAX_VALUE, 28));
actionBtn.setFocusable(false);
//actionBtn.putClientProperty("JButton.buttonType", "roundRect");
actionBtn.putClientProperty("JButton.buttonType", "segmentedCapsule");
actionBtn.putClientProperty("JButton.segmentPosition", "only");
actionBtn.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
if (chatPanel.isVisible()) {
cancelCreateChat();
} else
if (dialogsBtn.isSelected()) {
createChat();
} else
if (contactsBtn.isSelected()) {
addContact();
}
}
});
actionPanel.add(actionBtn);
actionPanel.add(Box.createRigidArea(new Dimension(6, 40)));
JLayeredPane layeredPane = new JLayeredPane();
layeredPane.setLayout(new OverlayLayout(layeredPane));
splitPane.setRightComponent(layeredPane);
contentPanel = new JPanel();
layeredPane.add(contentPanel, JLayeredPane.DEFAULT_LAYER);
contentPanel.setLayout(new BorderLayout(0, 0));
contentPanel.setAlignmentX(0);
titlePanel = new JPanel();
contentPanel.add(titlePanel, BorderLayout.NORTH);
titlePanel.setBackground(Color.decode("0xf9f9f9"));
titlePanel.setLayout(new GridBagLayout());
titlePanel.setPreferredSize(new Dimension(0, 40));
titleIcon = new ImagePanel();
titleIcon.setPreferredSize(new Dimension(40, 40));
titleIcon.setBorder(new EmptyBorder(4, 4, 4, 4));
titlePanel.add(titleIcon, Utils.GBConstraints(0, 0, 1, 2));
titleLabel = new JLabel();
GridBagConstraints constraints = Utils.GBConstraints(1, 0, 1, 1);
constraints.weightx = 1;
constraints.fill = GridBagConstraints.HORIZONTAL;
titleLabel.setFont(new Font(Utils.fontName, Font.PLAIN, 14));
titleLabel.setBorder(new EmptyBorder(3, 0, 0, 0));
titlePanel.add(titleLabel, constraints);
titleStatus = new JLabel();
constraints = Utils.GBConstraints(1, 1, 1, 1);
constraints.weightx = 1;
constraints.fill = GridBagConstraints.HORIZONTAL;
titleStatus.setForeground(Color.decode("0x808080"));
titlePanel.add(titleStatus, constraints);
titleInfoBtn = new JButton();
titleInfoBtn.setFocusable(false);
titleInfoBtn.putClientProperty("JButton.buttonType", "segmentedCapsule");
titleInfoBtn.putClientProperty("JButton.segmentPosition", "only");
titleInfoBtn.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
titleInfoBtn.setSelected(!titleInfoBtn.isSelected());
if (currentPeer != null) {
if (currentPeer instanceof InputPeerChat) {
if (titleInfoBtn.isSelected()) {
editChat();
} else {
cancelCreateChat();
}
} else {
toggleTitlePanel(titleInfoBtn.isSelected());
}
}
}
});
constraints = Utils.GBConstraints(2, 0, 1, 2);
constraints.anchor = GridBagConstraints.PAGE_START;
constraints.insets = new Insets(6, 0, 0, 0);
titlePanel.add(titleInfoBtn, constraints);
titlePanel.add(Box.createRigidArea(new Dimension(6, 40)), Utils.GBConstraints(3, 0, 1, 2));
titleActionBtn = new JButton();
titleActionBtn.setFocusable(false);
titleActionBtn.putClientProperty("JButton.buttonType", "segmentedCapsule");
titleActionBtn.putClientProperty("JButton.segmentPosition", "only");
titleActionBtn.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
if (currentPeer != null) {
if (currentPeer instanceof InputPeerChat) {
TChat chat = service.chatManager.get(currentPeer.chat_id);
if (!(chat instanceof ChatForbidden) && chat.left) {
service.mainServer.call(new tl.messages.AddChatUser(currentPeer.chat_id, new InputUserSelf(), 20), new RPCCallback<StatedMessage>() {
public void done(StatedMessage result) {
service.chatManager.store(result.chats);
service.userManager.store(result.users);
service.messageManager.store(result.message);
service.dialogManager.addMessage(result.message);
messageListModel.addMessage(result.message);
service.dialogManager.updateContents();
restoreDialogSelection();
titleInfoBtn.setVisible(true);
titleActionBtn.setText("покинуть чат");
sendPanel.setVisible(true);
}
public void error(int code, String message) {
}
});
} else
if (JOptionPane.showOptionDialog(frame, "Покинув чат, вы не сможете вернуться в него обратно.", "Подтвердите действие", JOptionPane.OK_CANCEL_OPTION, JOptionPane.WARNING_MESSAGE, null, new String[] { "Отмена", "Выйти из чата" }, "Отмена") == 1) {
//System.out.println("leaving");
titleActionBtn.setVisible(false);
sendPanel.setVisible(false);
service.mainServer.call(new tl.messages.DeleteChatUser(currentPeer.chat_id, new InputUserSelf()), new RPCCallback<StatedMessage>() {
public void done(StatedMessage result) {
service.chatManager.store(result.chats);
service.userManager.store(result.users);
service.messageManager.store(result.message);
service.dialogManager.addMessage(result.message);
messageListModel.addMessage(result.message);
service.dialogManager.updateContents();
restoreDialogSelection();
titleActionBtn.setVisible(!(service.chatManager.get(currentPeer.chat_id) instanceof ChatForbidden));
titleActionBtn.setText("вернуться в чат");
}
public void error(int code, String message) {
}
});
}
} else {
if (JOptionPane.showOptionDialog(frame, "Создать секретный чат с этим пользователем?", "Подтвердите действие", JOptionPane.OK_CANCEL_OPTION, JOptionPane.WARNING_MESSAGE, null, new String[] { "Отмена", "OK" }, "OK") == 1) {
createEncryptedChat(currentPeer.user_id);
}
}
}
}
});
constraints = Utils.GBConstraints(4, 0, 1, 2);
constraints.anchor = GridBagConstraints.PAGE_START;
constraints.insets = new Insets(6, 0, 0, 0);
titlePanel.add(titleActionBtn, constraints);
titlePanel.add(Box.createRigidArea(new Dimension(6, 40)), Utils.GBConstraints(5, 0, 1, 2));
messageList = new InteractiveList() {
public boolean getScrollableTracksViewportWidth() {
return true;
}
@Override
public int getScrollableUnitIncrement(Rectangle visibleRect,
int orientation, int direction) {
//return super.getScrollableUnitIncrement(visibleRect, orientation, direction);
return 30;
}
@Override
public int getScrollableBlockIncrement(Rectangle visibleRect,
int orientation, int direction) {
return super.getScrollableBlockIncrement(visibleRect, orientation, direction);
}
};
//System.out.println(messageList.getUI().toString());
messageList.setBackground(Color.decode("0xdfe8ef"));
messageList.addComponentListener(new ComponentListener() {
public void componentShown(ComponentEvent e) {
}
public void componentResized(ComponentEvent e) {
messageList.setFixedCellHeight(0);
messageList.setFixedCellHeight(-1);
//messageList.revalidate();
}
public void componentMoved(ComponentEvent e) {
}
public void componentHidden(ComponentEvent e) {
}
});
scrollPane = new JScrollPane(messageList);
scrollPane.setBorder(BorderFactory.createMatteBorder(1, 0, 1, 0, Color.decode("0xccd5db")));
scrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
scrollPane.setBackground(Color.decode("0xd6e4ef"));
scrollPane.getVerticalScrollBar().addAdjustmentListener(new AdjustmentListener() {
public void adjustmentValueChanged(AdjustmentEvent e) {
if (messageListModel != null && messageList != null && !e.getValueIsAdjusting()) {
messageListModel.scrolled(messageList.getFirstVisibleIndex(), messageList.getLastVisibleIndex());
}
}
});
messageListModel = new MessageListModel(service, messageList, null);
messageList.setModel(messageListModel);
messageList.setCellRenderer(new MessageCellRenderer(service, null));
messageList.setFocusable(false);
contentPanel.add(scrollPane, BorderLayout.CENTER);
sendPanel = new JPanel();
sendPanel.setLayout(new BoxLayout(sendPanel, BoxLayout.X_AXIS));
sendPanel.setBorder(new NinePatchBorder(Utils.getImage("input_border.png"), 12, 12, 12, 12, 10, 10, 11, 10));
sendPanel.setMinimumSize(new Dimension(0, 1));
contentPanel.add(sendPanel, BorderLayout.SOUTH);
messageField = new JTextField();
messageField.setBorder(new EmptyBorder(2, 2, 2, 2));
addHint(messageField, "Новое сообщение...");
sendPanel.add(messageField);
//textArea.setBorder(new JTextField().getBorder());
JButton attachBtn = new JButton(new ImageIcon(Utils.getImage("attach_photo.png")));
attachBtn.setRolloverIcon(new ImageIcon(Utils.getImage("attach_photo_highlight.png")));
attachBtn.setBorderPainted(false);
attachBtn.setOpaque(false);
attachBtn.setContentAreaFilled(false);
attachBtn.setBorder(null);
attachBtn.setPreferredSize(new Dimension(22, 18));
attachBtn.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
attachBtn.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
/*int result = fc.showOpenDialog(frame);
if (result == JFileChooser.APPROVE_OPTION) {
sendFiles(fc.getSelectedFiles());
}*/
fd.setVisible(true);
/*try {
File[] files = fd.getFiles();
if (files != null && files.length > 0) {
sendFiles(files);
}
} catch (Exception e1) {*/
String filename = fd.getFile();
if (filename != null) {
sendFiles(new File[]{ new File(fd.getDirectory() + System.getProperty("file.separator") + fd.getFile()) });
}
//}
}
});
sendPanel.add(attachBtn);
sendPanel.setVisible(false);
titlePanel.setVisible(false);
chatPanel = new JPanel();
layeredPane.add(chatPanel, JLayeredPane.PALETTE_LAYER);
chatPanel.setLayout(new BorderLayout());
chatPanel.setMinimumSize(new Dimension(30, 0));
chatPanel.setMaximumSize(new Dimension(320, Integer.MAX_VALUE));
chatPanel.setAlignmentX(0);
chatPanel.setBorder(BorderFactory.createMatteBorder(0, 0, 0, 1, Color.decode("0xe0e0e0")));
JPanel chatTitlePanel = new JPanel();
//actionPanel.setBorder(BorderFactory.createMatteBorder(1, 0, 0, 0, Color.decode("0xe0e0e0")));
chatTitlePanel.setBackground(Color.decode("0xf9f9f9"));
chatTitlePanel.setLayout(new BoxLayout(chatTitlePanel, BoxLayout.X_AXIS));
chatPanel.add(chatTitlePanel, BorderLayout.NORTH);
chatTitlePanel.add(Box.createRigidArea(new Dimension(4, 40)));
chatTitleField = new JTextField();
chatTitleField.setAlignmentY(Component.CENTER_ALIGNMENT);
chatTitleField.setMaximumSize(new Dimension(Integer.MAX_VALUE, 30));
chatTitlePanel.add(chatTitleField);
addHint(chatTitleField, "Название чата");
chatTitlePanel.add(Box.createRigidArea(new Dimension(4, 40)));
chatMemberList = new InteractiveList() {
public boolean getScrollableTracksViewportWidth() {
return true;
}
};
chatMemberList.setBackground(Color.WHITE);
memberListRenderer = new ContactListRenderer(service);
chatMemberList.setCellRenderer(memberListRenderer);
chatMemberList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
/*chatMemberList.addListSelectionListener(new ListSelectionListener() {
public void valueChanged(ListSelectionEvent e) {
if (chatMemberList.getSelectedIndex() > -1 && !chatMemberModel.isEmpty()) {
chatMemberModel.remove(chatMemberList.getSelectedIndex());
chatMemberList.clearSelection();
chatActionBtn.setEnabled(!chatTitleField.getForeground().equals(Color.LIGHT_GRAY) && !chatTitleField.getText().isEmpty() && !chatMemberModel.isEmpty());
}
}
});*/
scrollPane = new JScrollPane(chatMemberList);
scrollPane.setBackground(Color.decode("0xf9f9f9"));
scrollPane.setBorder(BorderFactory.createMatteBorder(1, 0, 1, 0, Color.decode("0xe0e0e0")));
scrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
chatPanel.add(scrollPane, BorderLayout.CENTER);
JPanel chatActionPanel = new JPanel();
//actionPanel.setBorder(BorderFactory.createMatteBorder(1, 0, 0, 0, Color.decode("0xe0e0e0")));
chatActionPanel.setBackground(Color.decode("0xf9f9f9"));
chatActionPanel.setLayout(new BoxLayout(chatActionPanel, BoxLayout.X_AXIS));
chatPanel.add(chatActionPanel, BorderLayout.SOUTH);
chatActionPanel.add(Box.createRigidArea(new Dimension(6, 40)));
chatActionBtn = new JButton("Создать чат");
chatActionBtn.setMaximumSize(new Dimension(Integer.MAX_VALUE, 28));
chatActionBtn.setFocusable(false);
//chatActionBtn.putClientProperty("JButton.buttonType", "gradient");
chatActionBtn.putClientProperty("JButton.buttonType", "segmentedCapsule");
chatActionBtn.putClientProperty("JButton.segmentPosition", "only");
chatActionBtn.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
if (currentPeer != null && (currentPeer instanceof InputPeerChat) && titleInfoBtn.isSelected()) {
saveChat();
} else {
doCreateChat();
}
}
});
chatActionPanel.add(chatActionBtn);
chatActionPanel.add(Box.createRigidArea(new Dimension(6, 40)));
chatTitleField.getDocument().addDocumentListener(new DocumentListener() {
public void removeUpdate(DocumentEvent e) {
chatActionBtn.setEnabled(!chatTitleField.getForeground().equals(Color.LIGHT_GRAY) && !chatTitleField.getText().isEmpty() && !chatMemberModel.isEmpty());
}
public void insertUpdate(DocumentEvent e) {
chatActionBtn.setEnabled(!chatTitleField.getForeground().equals(Color.LIGHT_GRAY) && !chatTitleField.getText().isEmpty() && !chatMemberModel.isEmpty());
}
public void changedUpdate(DocumentEvent e) {
chatActionBtn.setEnabled(!chatTitleField.getForeground().equals(Color.LIGHT_GRAY) && !chatTitleField.getText().isEmpty() && !chatMemberModel.isEmpty());
}
});
chatPanel.setVisible(false);
messageField.addKeyListener(new KeyListener() {
@Override
public void keyTyped(KeyEvent e) {
if (currentPeer != null && !messageField.getText().isEmpty()) {
service.typingManager.startTyping(currentPeer);
}
}
@Override
public void keyReleased(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_ENTER) {
String message = messageField.getText();
if (currentPeer != null && message.length() > 0 && !messageField.getForeground().equals(Color.LIGHT_GRAY)) {
sendMessage(message, currentPeer, currentEncryptedChat);
messageField.setText("");
}
}
}
@Override
public void keyPressed(KeyEvent e) {
}
});
new DropTarget(messageList, new DropTargetListener() {
public void dropActionChanged(DropTargetDragEvent dtde) {
if (currentPeer == null) return;
dtde.acceptDrag(DnDConstants.ACTION_COPY_OR_MOVE);
}
public void drop(DropTargetDropEvent dtde) {
if (currentPeer == null) return;
dtde.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
Transferable t = dtde.getTransferable();
try {
java.util.List<File> fileList = (java.util.List<File>) t.getTransferData(DataFlavor.javaFileListFlavor);
sendFiles(fileList.toArray(new File[1]));
} catch (Exception e) {
e.printStackTrace();
}
dtde.dropComplete(true);
}
public void dragOver(DropTargetDragEvent dtde) {
if (currentPeer == null) return;
dtde.acceptDrag(DnDConstants.ACTION_COPY_OR_MOVE);
}
public void dragExit(DropTargetEvent dte) {
}
public void dragEnter(DropTargetDragEvent dtde) {
if (currentPeer == null) return;
dtde.acceptDrag(DnDConstants.ACTION_COPY_OR_MOVE);
}
});
dialogList.addAncestorListener( new RequestFocusListener() );
if (!System.getProperty("os.name").contains("Mac")) {
addHint(searchField, "Найти...");
}
service.mainServer.call(new tl.account.UpdateStatus(false));
Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
public void run() {
service.mainServer.call(new tl.account.UpdateStatus(true));
}
}));
}
protected void createEncryptedChat(final int user_id) {
final TUser user = service.userManager.get(user_id);
service.mainServer.call(new tl.messages.GetDhConfig(0, 256), new RPCCallback<DhConfig>() {
public void done(final DhConfig config) {
final byte[] random = new byte[256];
(new Random()).nextBytes(random); // TODO: add more entropy
for (int i = 0; i < random.length; i++) {
random[i] ^= config.random[i];
}
BigInteger g = BigInteger.valueOf(config.g);
BigInteger a = new BigInteger(1, random);
BigInteger dh_prime = new BigInteger(1, config.p);
byte[] g_a = g.modPow(a, dh_prime).toByteArray();
int random_id = (int) (Math.random() * 10000000);
service.mainServer.call(new tl.messages.RequestEncryption(Utils.getInputUser(user), random_id, g_a), new RPCCallback<TEncryptedChat>() {
public void done(TEncryptedChat chat) {
service.dialogManager.addEncryptedChat(user_id, chat, random, config.p);
selectDialog(user, chat, new byte[256]);
}
public void error(int code, String message) {
if (message.equals("PARTICIPANT_VERSION_OUTDATED")) {
JOptionPane.showMessageDialog(frame, "Не удается создать секретный чат: собеседник использует устаревшую версию приложения.");
}
System.out.println("Failed to create encrypted chat: " + code + ", " + message);
}
});
}
public void error(int code, String message) {
System.out.println("Failed to get DH config: " + code + ", " + message);
}
});
}
protected void toggleTitlePanel(boolean visible) {
titlePanel.setMinimumSize(new Dimension(0, visible ? 100 : 40));
titlePanel.setPreferredSize(new Dimension(0, visible ? 100 : 40));
titleIcon.setMinimumSize(new Dimension(visible ? 100 : 40, visible ? 100 : 40));
titleIcon.setPreferredSize(new Dimension(visible ? 100 : 40, visible ? 100 : 40));
titleIcon.setBorder(new EmptyBorder(visible ? 6 : 4, visible ? 6 : 4, visible ? 6 : 4, visible ? 6 : 4));
int user_id = currentPeer.user_id;
TUser user = service.userManager.get(user_id);
String title = (user.first_name + " " + user.last_name).trim();
titleLabel.setBorder(new EmptyBorder(visible ? 8 : 3, 0, 0, 0));
titleLabel.setText(visible ? "<html>" + title + "<br/><br/>" + Utils.formatPhone(user.phone) + "</html>" : title);
}
protected void editChat() {
contactsState = contactsBtn.isSelected();
contactsBtn.doClick();
searchField.setText("");
chatPanel.setVisible(true);
dialogsBtn.setVisible(false);
contactsBtn.setVisible(false);
actionBtn.setText("Отмена");
chatActionBtn.setEnabled(false);
chatActionBtn.setText("Применить");
chatMemberModel = new ContactListModel(service, chatMemberList);
chatMemberModel.setMissingText("Выберите участников из списка слева");
if (currentChat != null) {
chatMemberModel.add(currentChat.participants.participants);
}
memberListRenderer.buttonText = "исключить";
memberListRenderer.dropCache();
contactListRenderer.buttonText = "пригласить";
contactListRenderer.dropCache();
memberListRenderer.actionListener = new ContactListRenderer.ContactActionListener() {
public void onContactSelected(final int user_id, int index) {
if (JOptionPane.showOptionDialog(frame, "Вы уверены, что хотите исключить участника из чата?", "Подтвердите действие", JOptionPane.OK_CANCEL_OPTION, JOptionPane.WARNING_MESSAGE, null, new String[] { "Отмена", "Исключить из чата" }, "Отмена") == 1) {
chatMemberModel.remove(index);
TUser user = service.userManager.get(user_id);
service.mainServer.call(new tl.messages.DeleteChatUser(currentPeer.chat_id, user instanceof UserContact ? new InputUserContact(user_id) : new InputUserForeign(user_id, user.access_hash)), new RPCCallback<StatedMessage>() {
public void done(StatedMessage result) {
service.chatManager.store(result.chats);
service.userManager.store(result.users);
service.messageManager.store(result.message);
service.dialogManager.addMessage(result.message);
messageListModel.addMessage(result.message);
contactListRenderer.dropCache(user_id);
contactListModel.updateContents(user_id);
service.dialogManager.updateContents();
restoreDialogSelection();
service.mainServer.call(new GetFullChat(currentPeer.chat_id), new Server.RPCCallback<tl.messages.ChatFull>() {
public void done(tl.messages.ChatFull result) {
if (result.full_chat.id == currentPeer.chat_id) {
service.userManager.store(result.users);
service.chatManager.store(result.chats);
currentChat = (ChatFull) result.full_chat;
updateStatus();
}
}
public void error(int code, String message) {
}
});
}
public void error(int code, String message) {
}
});
}
}
public boolean isVisible(int user_id, int index) {
if (currentChat == null || currentChat.participants == null) return false;
if (currentChat.participants.admin_id == service.me.id || user_id == service.me.id) {
return true;
}
for (TChatParticipant part : currentChat.participants.participants) {
if (part.user_id == user_id && part.inviter_id == service.me.id) {
return true;
}
}
return false;
}
};
contactListRenderer.actionListener = new ContactListRenderer.ContactActionListener() {
public void onContactSelected(final int user_id, int index) {
chatMemberModel.add(user_id, true);
TUser user = service.userManager.get(user_id);
service.mainServer.call(new tl.messages.AddChatUser(currentPeer.chat_id, user instanceof UserContact ? new InputUserContact(user_id) : new InputUserForeign(user_id, user.access_hash), 10), new RPCCallback<StatedMessage>() {
public void done(StatedMessage result) {
service.chatManager.store(result.chats);
service.userManager.store(result.users);
service.messageManager.store(result.message);
service.dialogManager.addMessage(result.message);
messageListModel.addMessage(result.message);
contactListRenderer.dropCache(user_id);
contactListModel.updateContents(user_id);
service.dialogManager.updateContents();
restoreDialogSelection();
service.mainServer.call(new GetFullChat(currentPeer.chat_id), new Server.RPCCallback<tl.messages.ChatFull>() {
public void done(tl.messages.ChatFull result) {
if (result.full_chat.id == currentPeer.chat_id) {
service.userManager.store(result.users);
service.chatManager.store(result.chats);
currentChat = (ChatFull) result.full_chat;
updateStatus();
}
}
public void error(int code, String message) {
}
});
}
public void error(int code, String message) {
}
});
}
@Override
public boolean isVisible(int user_id, int index) {
if (currentChat == null || currentChat.participants == null) return false;
for (TChatParticipant part : currentChat.participants.participants) {
if (part.user_id == user_id) {
return false;
}
}
return true;
}
};
chatMemberList.setModel(chatMemberModel);
TChat chat = service.chatManager.get(currentPeer.chat_id);
setHintedFieldText(chatTitleField, chat.title);
//chatTitleField.requestFocusInWindow();
}
protected void saveChat() {
if (currentPeer == null || !(currentPeer instanceof InputPeerChat)) return;
final String title = chatTitleField.getText();
if (title.length() == 0) return;
chatActionBtn.setEnabled(false);
service.mainServer.call(new tl.messages.EditChatTitle(currentPeer.chat_id, title), new RPCCallback<StatedMessage>() {
public void done(final StatedMessage result) {
service.chatManager.store(result.chats);
service.userManager.store(result.users);
service.messageManager.store(result.message);
service.dialogManager.addMessage(result.message);
messageListModel.addMessage(result.message);
service.dialogManager.updateContents();
titleLabel.setText(title);
cancelCreateChat();
}
public void error(int code, String message) {
//
}
});
}
private boolean contactsState;
private JButton chatActionBtn;
private JList chatMemberList;
private ContactListModel chatMemberModel;
private JPanel titlePanel;
private ImagePanel titleIcon;
private JPanel contentPanel;
private JTextField chatTitleField;
protected void cancelCreateChat() {
searchField.setText("");
chatPanel.setVisible(false);
dialogsBtn.setVisible(true);
contactsBtn.setVisible(true);
memberListRenderer.buttonText = null;
memberListRenderer.dropCache();
contactListRenderer.buttonText = null;
contactListRenderer.dropCache();
if (contactsState) {
contactsBtn.doClick();
} else {
dialogsBtn.doClick();
}
if (currentPeer != null && currentPeer instanceof InputPeerChat) {
titleInfoBtn.setSelected(false);
}
}
protected void doCreateChat() {
chatActionBtn.setEnabled(false);
String title = chatTitleField.getText();
if (title.length() == 0) return;
TInputUser[] users = new TInputUser[chatMemberModel.getSize()];
for (int i = 0; i < users.length; i++) {
users[i] = Utils.getInputUser(service.userManager.get((Integer) chatMemberModel.getElementAt(i)));
}
service.mainServer.call(new CreateChat(users, title), new RPCCallback<StatedMessage>() {
public void done(final StatedMessage result) {
service.chatManager.store(result.chats);
service.userManager.store(result.users);
service.dialogManager.addMessage(result.message);
service.dialogManager.updateContents();
selectDialog(result.chats[0]);
cancelCreateChat();
messageField.requestFocusInWindow();
}
public void error(int code, String message) {
//
}
});
}
protected void createChat() {
contactsState = contactsBtn.isSelected();
contactsBtn.doClick();
searchField.setText("");
chatPanel.setVisible(true);
dialogsBtn.setVisible(false);
contactsBtn.setVisible(false);
actionBtn.setText("Отмена");
chatActionBtn.setEnabled(false);
chatActionBtn.setText("Создать чат");
chatMemberModel = new ContactListModel(service, chatMemberList);
chatMemberModel.setMissingText("Выберите участников из списка слева");
memberListRenderer.buttonText = "удалить";
memberListRenderer.dropCache();
contactListRenderer.buttonText = "добавить";
contactListRenderer.dropCache();
memberListRenderer.actionListener = new ContactListRenderer.ContactActionListener() {
public void onContactSelected(final int user_id, int index) {
chatMemberModel.remove(index);
contactListRenderer.dropCache(user_id);
contactListModel.updateContents(user_id);
}
public boolean isVisible(int user_id, int index) {
return true;
}
};
contactListRenderer.actionListener = new ContactListRenderer.ContactActionListener() {
public void onContactSelected(final int user_id, int index) {
chatMemberModel.add(user_id, true);
contactListRenderer.dropCache(user_id);
contactListModel.updateContents(user_id);
}
public boolean isVisible(int user_id, int index) {
return !chatMemberModel.contains(user_id);
}
};
chatMemberList.setModel(chatMemberModel);
chatTitleField.requestFocusInWindow();
}
protected void importContacts() {
importContacts(false);
}
protected void importContacts(boolean replace) {
importDialog.setVisible(true);
String filename = importDialog.getFile();
ArrayList<String[]> contacts = new ArrayList<String[]>();
try {
BufferedReader cin = new BufferedReader(new InputStreamReader(new FileInputStream(importDialog.getDirectory() + System.getProperty("file.separator") + importDialog.getFile()), "UTF8"));
String line = cin.readLine();
if (line != null) {
boolean vcf = line.trim().equals("BEGIN:VCARD") || filename.endsWith(".vcf");
if (vcf) {
String phone = null;
String fname = "";
String lname = "";
while (true) {
line = cin.readLine();
if (line == null) break;
if (line.trim().equals("END:VCARD")) {
if (phone != null) {
contacts.add(new String[] { phone, fname, lname });
}
phone = null;
fname = "";
lname = "";
} else
if (line.startsWith("N:")) {
String[] comp = line.split(":", -1)[1].split(";", -1);
lname = comp[0];
fname = comp[1];
} else
if (line.startsWith("TEL:") || line.startsWith("TEL;")) {
String[] comp = line.split(":", -1);
phone = comp[1];
}
}
} else {
String[] comp = line.split(",", -1);
int phoneIndex = -1;
int fnameIndex = -1;
int lnameIndex = -1;
for (int i = 0; i < comp.length; i++) {
String head = comp[i].trim().toLowerCase();
if (head.equals("first name") || head.equals("given name")) {
fnameIndex = i;
} else
if (head.equals("last name") || head.equals("family name")) {
lnameIndex = i;
} else
if (head.equals("primary phone")) {
phoneIndex = i;
}
}
if (phoneIndex > -1) {
while (true) {
line = cin.readLine();
if (line == null) break;
comp = line.split(",", -1);
String[] cont = new String[] { phoneIndex > -1 ? comp[phoneIndex] : "", fnameIndex > -1 ? comp[fnameIndex] : "", lnameIndex > -1 ? comp[lnameIndex] : "" };
if (!cont[0].isEmpty()) {
contacts.add(cont);
}
}
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
for (String[] ss : contacts) {
System.out.println("phone: " + ss[0] + ", fn: " + ss[1] + ", ln: " + ss[2]);
}
if (contacts.size() > 0) {
importContacts(contacts, replace);
}
}
protected void addContact() {
JTextField phoneField = new JTextField(16);
phoneField.setText("+7");
phoneField.setCaretPosition(2);
JTextField firstField = new JTextField(16);
JTextField lastField = new JTextField(16);
JPanel myPanel = new JPanel();
GridBagConstraints constraints;
myPanel.setMaximumSize(new Dimension(200, Integer.MAX_VALUE));
myPanel.setLayout(new GridBagLayout());
JLabel label = new JLabel("<html>Укажите телефон и имя нового контакта. Если он уже зарегистрирован в Telegram, он будет добавлен в ваш список контактов.</html>");
label.setMaximumSize(new Dimension(320, Integer.MAX_VALUE));
label.setPreferredSize(new Dimension(320, 70));
myPanel.add(label, Utils.GBConstraints(0, 0, 2, 1));
constraints = Utils.GBConstraints(0, 1);
constraints.anchor = GridBagConstraints.LINE_END;
myPanel.add(new JLabel("Телефон:"), constraints);
myPanel.add(phoneField, Utils.GBConstraints(1, 1));
constraints = Utils.GBConstraints(0, 2);
constraints.anchor = GridBagConstraints.LINE_END;
myPanel.add(new JLabel("Имя:"), constraints);
myPanel.add(firstField, Utils.GBConstraints(1, 2));
constraints = Utils.GBConstraints(0, 3);
constraints.anchor = GridBagConstraints.LINE_END;
myPanel.add(new JLabel("Фамилия:"), constraints);
myPanel.add(lastField, Utils.GBConstraints(1, 3));
phoneField.addAncestorListener(new RequestFocusListener());
int result = JOptionPane.showConfirmDialog(frame, myPanel,
"Добавление контакта", JOptionPane.OK_CANCEL_OPTION);
if (result == JOptionPane.OK_OPTION) {
ArrayList<String[]> list = new ArrayList<String[]>();
list.add(new String[] { phoneField.getText(), firstField.getText(), lastField.getText() });
importContacts(list);
}
}
private boolean preventHintUpdates = false; // Hack
private ContactListRenderer memberListRenderer;
private void addHint(final JTextField field, final String hint) {
field.setForeground(Color.LIGHT_GRAY);
field.setText(hint);
field.addFocusListener(new FocusListener() {
public void focusLost(FocusEvent e) {
/*if (field.getText().isEmpty()) {
field.setForeground(Color.LIGHT_GRAY);
field.setText(hint);
}*/
}
public void focusGained(FocusEvent e) {
if (field.getForeground().equals(Color.LIGHT_GRAY)) {
/*field.setForeground(Color.BLACK);
field.setText("");*/
field.setCaretPosition(0);
}
}
});
field.addCaretListener(new CaretListener() {
public void caretUpdate(CaretEvent e) {
if (field.getForeground().equals(Color.LIGHT_GRAY) && e.getDot() > 0) {
field.setCaretPosition(0);
}
}
});
field.getDocument().addDocumentListener(new DocumentListener() {
boolean updating = false;
public void removeUpdate(final DocumentEvent e) {
if (updating || preventHintUpdates) return;
if (field.getText().isEmpty()) {
updating = true;
SwingUtilities.invokeLater(new Runnable() {
public void run() {
field.setForeground(Color.LIGHT_GRAY);
field.setText(hint);
field.setCaretPosition(0);
updating = false;
}
});
}
}
@Override
public void insertUpdate(final DocumentEvent e) {
if (updating || preventHintUpdates) return;
if (!field.getText().isEmpty() && field.getForeground().equals(Color.LIGHT_GRAY)) {
updating = true;
SwingUtilities.invokeLater(new Runnable() {
public void run() {
field.setForeground(Color.BLACK);
field.setText(field.getText().substring(e.getOffset(), e.getOffset() + e.getLength()));
updating = false;
}
});
}
}
@Override
public void changedUpdate(final DocumentEvent e) {
if (updating || preventHintUpdates) return;
if (field.getText().isEmpty()) {
updating = true;
SwingUtilities.invokeLater(new Runnable() {
public void run() {
field.setForeground(Color.LIGHT_GRAY);
field.setText(hint);
field.setCaretPosition(0);
updating = false;
}
});
} else
if (!field.getText().isEmpty() && field.getForeground().equals(Color.LIGHT_GRAY)) {
updating = true;
SwingUtilities.invokeLater(new Runnable() {
public void run() {
field.setForeground(Color.BLACK);
field.setText(field.getText().substring(e.getOffset(), e.getOffset() + e.getLength()));
updating = false;
}
});
}
}
});
}
public void sendMessage(String message, TInputPeer inputPeer, int encryptedChat) {
int random_id = -(new Random()).nextInt(0x10000000);
int peer_id = Utils.getPeerID(inputPeer, service.me);
TPeer peer;
if (peer_id > 0) {
peer = new PeerUser(peer_id);
} else {
peer = new PeerChat(-peer_id);
}
final TMessage newmsg = new Message(random_id, service.me.id, peer, true, true, (int) (System.currentTimeMillis() / 1000), message, new MessageMediaEmpty());
newmsg.sending = true;
service.messageManager.store(newmsg);
messageListModel.addMessage(newmsg);
restoreDialogSelection();
dialogList.repaint();
messageList.repaint();
TDecryptedMessage decrypted = null;
if (encryptedChat > -1) {
byte[] random_bytes;
if (message.length() > 16) {
random_bytes = new byte[0];
} else {
random_bytes = new byte[16];
(new Random()).nextBytes(random_bytes);
}
decrypted = new DecryptedMessage(random_id, random_bytes, message, new DecryptedMessageMediaEmpty());
sendEncryptedMessage(peer_id, encryptedChat, random_id, decrypted, newmsg);
return;
} else {
service.dialogManager.addMessage(newmsg);
}
service.mainServer.call(new SendMessage(currentPeer, message, random_id), new RPCCallback<SentMessage>() {
public void done(SentMessage result) {
newmsg.id = result.id;
newmsg.date = result.date;
newmsg.sending = false;
service.messageManager.store(newmsg);
restoreDialogSelection();
dialogList.repaint();
messageListModel.updateContentsID(result.id);
}
public void error(int code, String message) {
newmsg.failed = true;
}
});
}
public void sendEncryptedMessage(int user_id, final int encryptedChat, final int random_id, final TDecryptedMessage decrypted, final TMessage stub) {
EncryptedDialog dialog = service.dialogManager.chats.get(encryptedChat);
if (dialog != null) {
if (dialog.chat instanceof EncryptedChat) {
try {
//DecryptedMessageLayer layer = new DecryptedMessageLayer(8, decrypted);
TDecryptedMessage layer = decrypted;
int size = 4 + layer.length(true);
while (size % 16 != 0) size++;
ByteBuffer inner = ByteBuffer.allocate(size);
inner.order(ByteOrder.LITTLE_ENDIAN);
inner.putInt(layer.length(true));
layer.writeTo(inner, true);
while (inner.position() < size) inner.put((byte) 0);
inner.rewind();
byte[] msg_key = substr(SHA1(inner, 0, inner.capacity()), 4, 16);
int x = 0;
byte[] sha1_a = SHA1(concat(msg_key, substr(dialog.key, x, 32)));
byte[] sha1_b = SHA1(concat(substr(dialog.key, 32 + x, 16), msg_key, substr(dialog.key, 48 + x, 16)));
byte[] sha1_c = SHA1(concat(substr(dialog.key, 64 + x, 32), msg_key));
byte[] sha1_d = SHA1(concat(msg_key, substr(dialog.key, 96 + x, 32)));
byte[] aes_key = concat(substr(sha1_a, 0, 8), substr(sha1_b, 8, 12), substr(sha1_c, 4, 12));
byte[] aes_iv = concat(substr(sha1_a, 8, 12), substr(sha1_b, 0, 8), substr(sha1_c, 16, 4), substr(sha1_d, 0, 8));
ByteBuffer buffer = ByteBuffer.allocate(size + 24);
buffer.order(ByteOrder.LITTLE_ENDIAN);
buffer.putLong(dialog.chat.key_fingerprint);
buffer.put(msg_key);
buffer.put(AESEncrypt(inner, 0, size, aes_key, aes_iv));
buffer.rewind();
final byte[] bytes = new byte[buffer.capacity()];
buffer.get(bytes);
//TEncryptedMessage encrypted = new EncryptedMessage(random_id, dialog.chat.id, (int) ((new Date()).getTime() / 1000), bytes, new EncryptedFileEmpty());
//service.dialogManager.addEncryptedMessage(encryptedChat, service.me.id, encrypted, decrypted);
service.mainServer.call(new SendEncrypted(new InputEncryptedChat(encryptedChat, dialog.chat.access_hash), random_id, bytes), new RPCCallback<TSentEncryptedMessage>() {
public void done(TSentEncryptedMessage result) {
TEncryptedMessage encrypted = new EncryptedMessage(random_id, encryptedChat, result.date, bytes, new EncryptedFileEmpty());
service.dialogManager.addEncryptedMessage(encryptedChat, service.me.id, encrypted, decrypted);
stub.date = result.date;
stub.sending = false;
restoreDialogSelection();
dialogList.repaint();
messageListModel.updateContentsID(random_id);
}
public void error(int code, String message) {
stub.failed = true;
System.out.println("Error while sending encrypted message: " + code + " (" + message + ")");
}
});
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
public void selectDialog(TChat chat) {
selectDialog(new InputPeerChat(chat.id), null, null);
}
public void selectDialog(TUser user, TEncryptedChat chat, byte[] key) {
TInputPeer peer = null;
if (user instanceof UserForeign || user instanceof UserRequest) {
peer = new InputPeerForeign(user.id, user.access_hash);
} else {
peer = new InputPeerContact(user.id);
}
selectDialog(peer, chat, key);
}
public void selectDialog(final TInputPeer peer, TEncryptedChat echat, byte[] key) {
if (currentPeer != null &&
((peer instanceof InputPeerChat && currentPeer instanceof InputPeerChat && peer.chat_id == currentPeer.chat_id) ||
(!(peer instanceof InputPeerChat) && !(currentPeer instanceof InputPeerChat) && peer.user_id == currentPeer.user_id))) {
return;
}
currentPeer = peer;
currentEncryptedChat = (echat == null) ? -1 : echat.id;
messageListModel = new MessageListModel(service, messageList, peer, echat == null ? null : service.dialogManager.chats.get(echat.id));
messageList.setModel(messageListModel);
messageList.setCellRenderer(new MessageCellRenderer(service, peer));
titleInfoBtn.setSelected(false);
toggleTitlePanel(false);
if (peer instanceof InputPeerChat) {
int chat_id = peer.chat_id;
TChat chat = service.chatManager.get(chat_id);
//System.out.println(chat);
service.chatManager.getImage(chat_id, titleIcon, false);
titleLabel.setText(chat.title.trim());
service.mainServer.call(new GetFullChat(chat_id), new Server.RPCCallback<tl.messages.ChatFull>() {
public void done(tl.messages.ChatFull result) {
if (result.full_chat.id == currentPeer.chat_id) {
service.userManager.store(result.users);
service.chatManager.store(result.chats);
currentChat = (ChatFull) result.full_chat;
updateStatus();
}
}
public void error(int code, String message) {
}
});
titleInfoBtn.setText("управление...");
titleActionBtn.setText(chat.left ? "вернуться в чат" : "покинуть чат");
titleActionBtn.setVisible(!(chat instanceof ChatForbidden));
sendPanel.setVisible(!(chat instanceof ChatForbidden) && !chat.left);
titleInfoBtn.setVisible(!(chat instanceof ChatForbidden) && !chat.left);
} else {
int user_id = peer.user_id;
TUser user = service.userManager.get(user_id);
service.userManager.getUserpic(user_id, titleIcon, false);
titleLabel.setText((user.first_name + " " + user.last_name).trim());
titleInfoBtn.setText("информация...");
titleInfoBtn.setVisible(true);
titleActionBtn.setText("создать секретный чат");
titleActionBtn.setVisible(true);
sendPanel.setVisible((echat == null) || (echat instanceof EncryptedChat));
}
currentChat = null;
updateStatus();
service.typingManager.callback = this;
titlePanel.setVisible(true);
}
public void updateStatus() {
if (currentPeer == null) return;
if (currentPeer instanceof InputPeerChat) {
int chat_id = currentPeer.chat_id;
TChat chat = service.chatManager.get(chat_id);
String status = service.typingManager.getStatus(-chat_id, true);
if (status == null) {
String online = "";
if (currentChat != null) {
int num = Utils.getChatOnline(service, currentChat);
if (num > 0) {
online = ", " + num + " в сети";
}
}
titleStatus.setText(Utils.num(chat.participants_count, new String[] { " участник", " участника", " участников" }, true) + online);
} else {
titleStatus.setText(status);
}
} else {
int user_id = currentPeer.user_id;
TUser user = service.userManager.get(user_id);
service.userManager.getUserpic(user_id, titleIcon, false);
String status = service.typingManager.getStatus(user_id, true);
titleStatus.setText(status == null ? Utils.toStatus(user.status, true) : status);
}
}
public void selectDialog(tl.Dialog dialog) {
if (dialog instanceof EncryptedDialog) {
selectDialog(service.userManager.get(((PeerUser) dialog.peer).user_id), ((EncryptedDialog) dialog).chat, ((EncryptedDialog) dialog).key);
} else
if (dialog.peer instanceof PeerUser) {
selectDialog(service.userManager.get(((PeerUser) dialog.peer).user_id), null, null);
} else {
selectDialog(new InputPeerChat(((PeerChat) dialog.peer).chat_id), null, null);
}
}
public void authorized(TUser user) {
service.logged((UserSelf) user);
service.dialogManager.reloadDialogs();
contactListModel.reload();
}
@Override
public void onNewMessage(TMessage message, boolean fresh) {
// TODO Auto-generated method stub
restoreDialogSelection();
service.dialogManager.updateContents();
if (currentPeer != null &&
((message.to_id instanceof PeerChat && currentPeer instanceof InputPeerChat && message.to_id.chat_id == currentPeer.chat_id) ||
(message.to_id instanceof PeerUser && !(currentPeer instanceof InputPeerChat) && (message.to_id.user_id == currentPeer.user_id || message.from_id == currentPeer.user_id)))) {
messageListModel.addMessage(message);
//messageList
service.dialogManager.resetUnread(currentPeer);
service.mainServer.call(new ReadHistory(currentPeer, message.id, 0), new Server.RPCCallback<TLObject>() {
public void done(TLObject result) {
}
public void error(int code, String message) {
}
});
}
}
@Override
public void onMessageID(int id, long random_id, boolean fresh) {
// TODO Auto-generated method stub
}
@Override
public void onReadMessages(int[] messages, boolean fresh) {
if (currentPeer == null) return;
//dialogListModel.updateContentsID(messages);
messageListModel.updateContentsID(messages);
}
@Override
public void onDeleteMessages(int[] messages, boolean fresh) {
restoreDialogSelection();
if (currentPeer == null) return;
//dialogListModel.updateContents();
}
@Override
public void onRestoreMessages(int[] messages, boolean fresh) {
// TODO Auto-generated method stub
restoreDialogSelection();
if (currentPeer == null) return;
//dialogListModel.updateContents();
}
@Override
public void onUserTyping(int user_id, boolean fresh) {
// TODO Auto-generated method stub
}
@Override
public void onChatUserTyping(int chat_id, int user_id, boolean fresh) {
// TODO Auto-generated method stub
}
@Override
public void onChatParticipants(TChatParticipants participants, boolean fresh) {
// TODO Auto-generated method stub
}
@Override
public void onUserStatus(int user_id, TUserStatus status, boolean fresh) {
// TODO Auto-generated method stub
contactListRenderer.dropCache(user_id);
memberListRenderer.dropCache(user_id);
contactListModel.updateContents(user_id);
updateStatus();
}
@Override
public void onUserName(int user_id, String first_name, String last_name,
boolean fresh) {
contactListRenderer.dropCache(user_id);
memberListRenderer.dropCache(user_id);
contactListModel.updateContents(user_id);
}
@Override
public void onUserPhoto(int user_id, TUserProfilePhoto photo, boolean fresh) {
contactListRenderer.dropCache(user_id);
memberListRenderer.dropCache(user_id);
contactListModel.updateContents(user_id);
}
@Override
public void onContactRegistered(int user_id, int date, boolean fresh) {
contactListModel.add(user_id, true);
}
public void onContactLink(int user_id, TMyLink my_link,
TForeignLink foreign_link, boolean fresh) {
// TODO Auto-generated method stub
}
@Override
public void onActivation(int user_id, boolean fresh) {
// TODO Auto-generated method stub
}
@Override
public void onNewAuthorization(long auth_key_id, int date, String device,
String location, boolean fresh) {
// TODO Auto-generated method stub
}
public void restoreDialogSelection() {
if (currentPeer == null) return;
if (dialogsBtn.isSelected()) {
if (service.dialogManager.isEmpty()) return;
for (int i = 0; i < service.dialogManager.getSize(); i++) {
tl.Dialog d = (tl.Dialog) service.dialogManager.getElementAt(i);
if ((currentEncryptedChat == -1 && !(d instanceof EncryptedDialog)) ||
(currentEncryptedChat > -1 && d instanceof EncryptedDialog && currentEncryptedChat == ((EncryptedDialog) d).chat.id)) {
if ((d.peer instanceof PeerChat && currentPeer instanceof InputPeerChat && d.peer.chat_id == currentPeer.chat_id) ||
(!(d.peer instanceof PeerChat) && !(currentPeer instanceof InputPeerChat) && d.peer.user_id == currentPeer.user_id)) {
dialogList.setSelectedIndex(i);
break;
}
}
}
} else {
if (contactListModel.isEmpty()) return;
for (int i = 0; i < contactListModel.getSize(); i++) {
int user_id = (Integer) contactListModel.getElementAt(i);
if (!(currentPeer instanceof InputPeerChat) && user_id == currentPeer.user_id) {
dialogList.setSelectedIndex(i);
return;
}
}
//dialogList.setSelectedIndex(-1);
}
}
private void sendFile(final File file) {
if (currentPeer == null) return;
final TInputPeer peer = currentPeer;
final int random_id = -(new Random()).nextInt(0x10000000);
boolean isPhoto = false;
Image bitmap = null;
TPhotoSize size = null;
try {
bitmap = ImageIO.read(file);
isPhoto = bitmap != null;
if (isPhoto) {
size = new PhotoSize("x", new FileLocation(service.mainServerID, 0, 0, random_id), bitmap.getWidth(null), bitmap.getHeight(null), 0);
}
} catch (Exception e) {
// not image
}
int peer_id = Utils.getPeerID(peer, service.me);
final TMessage futureMessage = new Message(
random_id,
service.me.id,
peer_id > 0 ? new PeerUser(peer_id) : new PeerChat(-peer_id),
true,
true,
(int) (System.currentTimeMillis() / 1000),
"",
isPhoto ?
new MessageMediaPhoto(new Photo(random_id, 0, service.me.id, (int) (System.currentTimeMillis() / 1000), "", new GeoPointEmpty(), new TPhotoSize[] { size })) :
new MessageMediaVideo(new Video(random_id, 0, service.me.id, (int) (System.currentTimeMillis() / 1000), "", 0, 0, size, service.mainServerID, 0, 0)));
futureMessage.sending = true;
futureMessage.preview = isPhoto ? bitmap : null;
service.messageManager.store(futureMessage);
service.dialogManager.addMessage(futureMessage);
messageListModel.addMessage(futureMessage);
restoreDialogSelection();
// 2. start upload
//ByteArrayOutputStream output = new ByteArrayOutputStream();
//bitmap.compress(CompressFormat.JPEG, 85, output);
try {
service.fileManager.upload(random_id, IOUtils.readFully(new FileInputStream(file), -1, true), file.getName(), new FileUploadingProgressiveCallback() {
public void fail() {
System.out.println("Unable to upload photo");
futureMessage.failed = true;
if (currentPeer != null && Utils.getPeerID(currentPeer, service.me) == Utils.getPeerID(peer, service.me)) {
messageListModel.updateContents();
}
}
public void progress(int loaded, int size, final float percent) {
/*if (futureMessage.row != null && futureMessage.row.get() != null) {
ProgressBar progress = ViewHolder.get(futureMessage.row.get(), R.id.progress);
progress.setProgress((int) (percent * 65535));
}*/
}
public void complete(TInputFile ifile) {
System.out.println("uploaded " + file.getName());
// 3. send message with uploaded image to server
service.mainServer.call(new SendMedia(peer, new InputMediaUploadedPhoto(ifile), random_id), new RPCCallback<StatedMessage>() {
public void done(StatedMessage result) {
// 4. replace fake message with real one
futureMessage.id = result.message.id;
futureMessage.date = result.message.date;
futureMessage.sending = false;
futureMessage.media = result.message.media;
//futureMessage.preview = null;
service.messageManager.store(futureMessage);
if (currentPeer != null && Utils.getPeerID(currentPeer, service.me) == Utils.getPeerID(peer, service.me)) {
messageListModel.updateContents();
}
}
public void error(int code, String message) {
futureMessage.failed = true;
if (currentPeer != null && Utils.getPeerID(currentPeer, service.me) == Utils.getPeerID(peer, service.me)) {
messageListModel.updateContents();
}
}
});
}
});
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
private void setHintedFieldText(JTextField field, String text) {
preventHintUpdates = true;
field.setForeground(Color.BLACK);
field.setText(text);
preventHintUpdates = false;
}
private void sendFiles(File[] files) {
if (currentPeer == null) return;
System.out.println("sending " + files.length + " files...");
for (File file : files) {
sendFile(file);
}
}
@Override
public void update(int peer_id) { // Callback from TypingManager
if ((currentPeer instanceof InputPeerChat && currentPeer.chat_id == -peer_id) ||
(!(currentPeer instanceof InputPeerChat) && currentPeer.chat_id == peer_id)) {
updateStatus();
}
}
public void importContacts(ArrayList<String[]> contacts) {
importContacts(contacts, false);
}
public void importContacts(ArrayList<String[]> contacts, boolean replace) {
TInputContact[] arr = new TInputContact[contacts.size()];
for (int i = 0; i < contacts.size(); i++) {
String[] contact = contacts.get(i);
arr[i] = new InputPhoneContact(i, contact[0], contact[1], contact[2]);
}
service.mainServer.call(new tl.contacts.ImportContacts(arr, replace), new Server.RPCCallback<ImportedContacts>() {
public void done(ImportedContacts result) {
contactListModel.add(result);
}
@Override
public void error(int code, String message) {
}
});
}
public void onNewEncryptedMessage(TEncryptedMessage encrypted, TDecryptedMessage message, boolean fresh) {
// TODO Auto-generated method stub
restoreDialogSelection();
service.dialogManager.updateContents();
if (currentEncryptedChat == encrypted.chat_id) {
messageListModel.addMessage(encrypted, message);
//messageList
//service.dialogManager.resetEncryptedUnread(currentPeer);
/*service.mainServer.call(new ReadHistory(currentPeer, message.id, 0), new Server.RPCCallback<TLObject>() {
public void done(TLObject result) {
}
public void error(int code, String message) {
}
});*/
}
}
@Override
public void onEncryptedChatTyping(int chat_id, boolean fresh) {
}
@Override
public void onEncryption(final TEncryptedChat chat, int date, boolean fresh) {
if (chat instanceof EncryptedChatRequested) { // Auto accept request (TODO: show dialog?)
service.mainServer.call(new tl.messages.GetDhConfig(0, 256), new RPCCallback<DhConfig>() {
public void done(final DhConfig config) {
final byte[] random = new byte[256];
(new Random()).nextBytes(random); // TODO: add more entropy
for (int i = 0; i < random.length; i++) {
random[i] ^= config.random[i];
}
BigInteger g_a = new BigInteger(1, chat.g_a);
BigInteger g = BigInteger.valueOf(config.g);
BigInteger b = new BigInteger(1, random);
BigInteger dh_prime = new BigInteger(1, config.p);
byte[] g_b = g.modPow(b, dh_prime).toByteArray();
byte[] g_ab = g_a.modPow(b, dh_prime).toByteArray();
final byte[] key = new byte[256];
for (int i = 0; i < chat.nonce.length; i++) {
key[i] = (byte) (g_ab[i + (g_ab.length - chat.nonce.length)] ^ chat.nonce[i]);
}
ByteBuffer tmp;
try {
tmp = ByteBuffer.wrap(SHA1(key));
tmp.order(ByteOrder.LITTLE_ENDIAN);
chat.key_fingerprint = tmp.getLong(12);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
service.mainServer.call(new tl.messages.AcceptEncryption(new InputEncryptedChat(chat.id, chat.access_hash), g_b, chat.key_fingerprint), new RPCCallback<TEncryptedChat>() {
public void done(TEncryptedChat chat) {
service.dialogManager.updateEncryptedChat(chat, key);
//selectDialog(service.userManager.get(chat.admin_id), chat, key);
}
public void error(int code, String message) {
if (message.equals("PARTICIPANT_VERSION_OUTDATED")) {
JOptionPane.showMessageDialog(frame, "Не удается создать секретный чат: собеседник использует устаревшую версию приложения.");
}
System.out.println("Failed to create encrypted chat: " + code + ", " + message);
}
});
}
public void error(int code, String message) {
System.out.println("Failed to get DH config: " + code + ", " + message);
}
});
//service.mainServer.call(new tl.messages.AcceptEncryption(peer, g_b, key_fingerprint));
}
}
@Override
public void onEncryptedMessagesRead(int chat_id, int max_date, int date, boolean fresh) {
}
}