Java identicon support.
This commit is contained in:
parent
ea9d8cc275
commit
70f7fa1345
@ -0,0 +1,106 @@
|
||||
package com.lyndir.masterpassword;
|
||||
|
||||
import static com.lyndir.lhunath.opal.system.util.StringUtils.strf;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.lyndir.lhunath.opal.system.MessageAuthenticationDigests;
|
||||
import com.lyndir.lhunath.opal.system.logging.Logger;
|
||||
import java.awt.*;
|
||||
import java.nio.*;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Arrays;
|
||||
import java.util.Map;
|
||||
|
||||
|
||||
/**
|
||||
* @author lhunath, 15-03-29
|
||||
*/
|
||||
public class MPIdenticon {
|
||||
|
||||
@SuppressWarnings("UnusedDeclaration")
|
||||
private static final Logger logger = Logger.get( MPIdenticon.class );
|
||||
|
||||
private static final Charset charset = StandardCharsets.UTF_8;
|
||||
private static final Color[] colors = new Color[]{
|
||||
Color.RED, Color.GREEN, Color.YELLOW, Color.BLUE, Color.MAGENTA, Color.CYAN, Color.MONO };
|
||||
private static final char[] leftArm = new char[]{ '╔', '╚', '╰', '═' };
|
||||
private static final char[] rightArm = new char[]{ '╗', '╝', '╯', '═' };
|
||||
private static final char[] body = new char[]{ '█', '░', '▒', '▓', '☺', '☻' };
|
||||
private static final char[] accessory = new char[]{
|
||||
'◈', '◎', '◐', '◑', '◒', '◓', '☀', '☁', '☂', '☃', '☄', '★', '☆', '☎', '☏', '⎈', '⌂', '☘', '☢', '☣', '☕', '⌚', '⌛', '⏰', '⚡',
|
||||
'⛄', '⛅', '☔', '♔', '♕', '♖', '♗', '♘', '♙', '♚', '♛', '♜', '♝', '♞', '♟', '♨', '♩', '♪', '♫', '⚐', '⚑', '⚔', '⚖', '⚙', '⚠',
|
||||
'⌘', '⏎', '✄', '✆', '✈', '✉', '✌' };
|
||||
|
||||
private final String fullName;
|
||||
private final Color color;
|
||||
private final String text;
|
||||
|
||||
public MPIdenticon(String fullName, String masterPassword) {
|
||||
this( fullName, masterPassword.toCharArray() );
|
||||
}
|
||||
|
||||
public MPIdenticon(String fullName, char[] masterPassword) {
|
||||
this.fullName = fullName;
|
||||
|
||||
byte[] masterPasswordBytes = charset.encode( CharBuffer.wrap( masterPassword ) ).array();
|
||||
ByteBuffer identiconSeedBytes = ByteBuffer.wrap(
|
||||
MessageAuthenticationDigests.HmacSHA256.of( masterPasswordBytes, fullName.getBytes( charset ) ) );
|
||||
Arrays.fill( masterPasswordBytes, (byte) 0 );
|
||||
|
||||
IntBuffer identiconSeedBuffer = IntBuffer.allocate( identiconSeedBytes.capacity() );
|
||||
while (identiconSeedBytes.hasRemaining())
|
||||
identiconSeedBuffer.put( identiconSeedBytes.get() & 0xFF );
|
||||
int[] identiconSeed = identiconSeedBuffer.array();
|
||||
|
||||
color = colors[identiconSeed[4] % colors.length];
|
||||
text = strf( "%c%c%c%c", leftArm[identiconSeed[0] % leftArm.length], body[identiconSeed[1] % body.length],
|
||||
rightArm[identiconSeed[2] % rightArm.length], accessory[identiconSeed[3] % accessory.length] );
|
||||
}
|
||||
|
||||
public String getFullName() {
|
||||
return fullName;
|
||||
}
|
||||
|
||||
public String getText() {
|
||||
return text;
|
||||
}
|
||||
|
||||
public Color getColor() {
|
||||
return color;
|
||||
}
|
||||
|
||||
public enum BackgroundMode {
|
||||
DARK, LIGHT
|
||||
}
|
||||
|
||||
|
||||
public enum Color {
|
||||
RED( "#dc322f", "#dc322f" ),
|
||||
GREEN( "#859900", "#859900" ),
|
||||
YELLOW( "#b58900", "#b58900" ),
|
||||
BLUE( "#268bd2", "#268bd2" ),
|
||||
MAGENTA( "#d33682", "#d33682" ),
|
||||
CYAN( "#2aa198", "#2aa198" ),
|
||||
MONO( "#93a1a1", "#586e75" );
|
||||
|
||||
private final String rgbDark;
|
||||
private final String rgbLight;
|
||||
|
||||
Color(final String rgbDark, final String rgbLight) {
|
||||
this.rgbDark = rgbDark;
|
||||
this.rgbLight = rgbLight;
|
||||
}
|
||||
|
||||
public java.awt.Color getAWTColor(BackgroundMode backgroundMode) {
|
||||
switch (backgroundMode) {
|
||||
case DARK:
|
||||
return new java.awt.Color( Integer.decode( rgbDark ) );
|
||||
case LIGHT:
|
||||
return new java.awt.Color( Integer.decode( rgbLight ) );
|
||||
}
|
||||
|
||||
throw new UnsupportedOperationException( "Unsupported background mode: " + backgroundMode );
|
||||
}
|
||||
}
|
||||
}
|
@ -65,6 +65,7 @@ public abstract class MasterKey {
|
||||
@Nonnull
|
||||
protected byte[] getKey() {
|
||||
|
||||
Preconditions.checkState( isValid() );
|
||||
return Preconditions.checkNotNull( masterKey );
|
||||
}
|
||||
|
||||
|
@ -1,9 +1,13 @@
|
||||
package com.lyndir.masterpassword.gui;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.lyndir.masterpassword.MPIdenticon;
|
||||
import com.lyndir.masterpassword.gui.util.Components;
|
||||
import java.awt.*;
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.swing.*;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
|
||||
/**
|
||||
@ -29,6 +33,8 @@ public abstract class AuthenticationPanel extends Components.GradientPanel {
|
||||
}
|
||||
} );
|
||||
add( Box.createVerticalGlue() );
|
||||
|
||||
avatarLabel.setToolTipText( "The avatar for your user. Click to change it." );
|
||||
}
|
||||
|
||||
protected void updateUser(boolean repack) {
|
||||
@ -39,8 +45,11 @@ public abstract class AuthenticationPanel extends Components.GradientPanel {
|
||||
unlockFrame.repack();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected abstract User getSelectedUser();
|
||||
|
||||
@NotNull
|
||||
@Nonnull
|
||||
public abstract char[] getMasterPassword();
|
||||
|
||||
public Component getFocusComponent() {
|
||||
|
@ -7,6 +7,7 @@ import java.awt.event.ActionListener;
|
||||
import javax.swing.*;
|
||||
import javax.swing.event.DocumentEvent;
|
||||
import javax.swing.event.DocumentListener;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
|
||||
/**
|
||||
@ -58,6 +59,7 @@ public class IncognitoAuthenticationPanel extends AuthenticationPanel implements
|
||||
return new IncognitoUser( fullNameField.getText() );
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public char[] getMasterPassword() {
|
||||
return masterPasswordField.getPassword();
|
||||
|
@ -14,6 +14,7 @@ import javax.swing.*;
|
||||
import javax.swing.event.DocumentEvent;
|
||||
import javax.swing.event.DocumentListener;
|
||||
import javax.swing.plaf.metal.MetalComboBoxEditor;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
|
||||
/**
|
||||
@ -104,6 +105,7 @@ public class ModelAuthenticationPanel extends AuthenticationPanel implements Ite
|
||||
return userField.getModel().getElementAt( selectedIndex );
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public char[] getMasterPassword() {
|
||||
return masterPasswordField.getPassword();
|
||||
@ -124,6 +126,7 @@ public class ModelAuthenticationPanel extends AuthenticationPanel implements Ite
|
||||
updateUser( true );
|
||||
}
|
||||
} );
|
||||
setToolTipText( "Add a new user to the list." );
|
||||
}
|
||||
}, new JButton( Res.iconQuestion() ) {
|
||||
{
|
||||
@ -131,10 +134,11 @@ public class ModelAuthenticationPanel extends AuthenticationPanel implements Ite
|
||||
@Override
|
||||
public void actionPerformed(final ActionEvent e) {
|
||||
JOptionPane.showMessageDialog( ModelAuthenticationPanel.this, //
|
||||
"Reads users and sites from the directory at ~/.mpw.", //
|
||||
"Reads users and sites from the directory at ~/.mpw.d.", //
|
||||
"Help", JOptionPane.INFORMATION_MESSAGE );
|
||||
}
|
||||
} );
|
||||
setToolTipText( "More information." );
|
||||
}
|
||||
} );
|
||||
}
|
||||
|
@ -53,7 +53,7 @@ public class PasswordFrame extends JFrame implements DocumentListener {
|
||||
add( Components.borderPanel( sitePanel, BorderFactory.createRaisedBevelBorder(), Res.colors().frameBg() ), BorderLayout.CENTER );
|
||||
|
||||
// User
|
||||
sitePanel.add( Components.label( strf( "Generating passwords for: %s", user.getFullName() ), JLabel.CENTER ) );
|
||||
sitePanel.add( Components.label( strf( "Generating passwords for: %s", user.getFullName() ), SwingConstants.CENTER ) );
|
||||
sitePanel.add( Components.stud() );
|
||||
|
||||
// Site Name
|
||||
@ -161,7 +161,7 @@ public class PasswordFrame extends JFrame implements DocumentListener {
|
||||
updateMask();
|
||||
|
||||
// Tip
|
||||
tipLabel = Components.label( " ", JLabel.CENTER );
|
||||
tipLabel = Components.label( " ", SwingConstants.CENTER );
|
||||
tipLabel.setAlignmentX( Component.CENTER_ALIGNMENT );
|
||||
JPanel passwordContainer = Components.boxLayout( BoxLayout.PAGE_AXIS, maskPasswordField, passwordField, tipLabel );
|
||||
passwordContainer.setOpaque( true );
|
||||
|
@ -97,6 +97,10 @@ public abstract class Res {
|
||||
return 19;
|
||||
}
|
||||
|
||||
public static Font emoticonsFont() {
|
||||
return emoticonsRegular();
|
||||
}
|
||||
|
||||
public static Font controlFont() {
|
||||
return arimoRegular();
|
||||
}
|
||||
@ -109,6 +113,10 @@ public abstract class Res {
|
||||
return sourceSansProBlack();
|
||||
}
|
||||
|
||||
public static Font emoticonsRegular() {
|
||||
return font( "fonts/Emoticons-Regular.otf" );
|
||||
}
|
||||
|
||||
public static Font sourceCodeProRegular() {
|
||||
return font( "fonts/SourceCodePro-Regular.otf" );
|
||||
}
|
||||
|
@ -2,10 +2,12 @@ package com.lyndir.masterpassword.gui;
|
||||
|
||||
import static com.lyndir.lhunath.opal.system.util.ObjectUtils.*;
|
||||
|
||||
import com.lyndir.masterpassword.MPIdenticon;
|
||||
import com.lyndir.masterpassword.gui.util.Components;
|
||||
import com.lyndir.masterpassword.model.IncorrectMasterPasswordException;
|
||||
import java.awt.*;
|
||||
import java.awt.event.*;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.swing.*;
|
||||
|
||||
|
||||
@ -16,6 +18,7 @@ public class UnlockFrame extends JFrame {
|
||||
|
||||
private final SignInCallback signInCallback;
|
||||
private final Components.GradientPanel root;
|
||||
private final JLabel identiconLabel;
|
||||
private final JButton signInButton;
|
||||
private final JPanel authenticationContainer;
|
||||
private AuthenticationPanel authenticationPanel;
|
||||
@ -28,9 +31,6 @@ public class UnlockFrame extends JFrame {
|
||||
this.signInCallback = signInCallback;
|
||||
|
||||
setDefaultCloseOperation( DISPOSE_ON_CLOSE );
|
||||
setContentPane(root = Components.gradientPanel(new BorderLayout(20, 20), Res.colors().frameBg()));
|
||||
root.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20));
|
||||
|
||||
addWindowFocusListener( new WindowAdapter() {
|
||||
@Override
|
||||
public void windowGainedFocus(WindowEvent e) {
|
||||
@ -43,17 +43,27 @@ public class UnlockFrame extends JFrame {
|
||||
}
|
||||
} );
|
||||
|
||||
authenticationContainer = Components.boxLayout( BoxLayout.PAGE_AXIS );
|
||||
authenticationContainer.setOpaque( true );
|
||||
authenticationContainer.setBackground( Res.colors().controlBg() );
|
||||
authenticationContainer.setBorder( BorderFactory.createEmptyBorder( 20, 20, 20, 20 ) );
|
||||
add( Components.borderPanel( authenticationContainer, BorderFactory.createRaisedBevelBorder(), Res.colors().frameBg() ) );
|
||||
|
||||
// Sign In
|
||||
JPanel signInBox = Components.boxLayout( BoxLayout.LINE_AXIS, Box.createGlue(), signInButton = Components.button( "Sign In" ),
|
||||
Box.createGlue() );
|
||||
signInBox.setBackground( null );
|
||||
root.add( signInBox, BorderLayout.SOUTH );
|
||||
|
||||
setContentPane( root = Components.gradientPanel( new FlowLayout(), Res.colors().frameBg() ) );
|
||||
root.setLayout( new BoxLayout( root, BoxLayout.PAGE_AXIS ) );
|
||||
root.setBorder( BorderFactory.createEmptyBorder( 20, 20, 20, 20 ) );
|
||||
root.add( Components.borderPanel( authenticationContainer = Components.boxLayout( BoxLayout.PAGE_AXIS ),
|
||||
BorderFactory.createRaisedBevelBorder(), Res.colors().frameBg() ) );
|
||||
root.add( Box.createVerticalStrut( 8 ) );
|
||||
root.add( identiconLabel = Components.label( " ", SwingConstants.CENTER ) );
|
||||
root.add( Box.createVerticalStrut( 8 ) );
|
||||
root.add( signInBox );
|
||||
|
||||
authenticationContainer.setOpaque( true );
|
||||
authenticationContainer.setBackground( Res.colors().controlBg() );
|
||||
authenticationContainer.setBorder( BorderFactory.createEmptyBorder( 20, 20, 20, 20 ) );
|
||||
identiconLabel.setFont( Res.emoticonsFont().deriveFont( 14.f ) );
|
||||
identiconLabel.setToolTipText(
|
||||
"A representation of your identity across all Master Password apps.\nIt should always be the same." );
|
||||
signInButton.addActionListener( new AbstractAction() {
|
||||
@Override
|
||||
public void actionPerformed(final ActionEvent e) {
|
||||
@ -86,6 +96,7 @@ public class UnlockFrame extends JFrame {
|
||||
authenticationContainer.add( Components.stud() );
|
||||
|
||||
final JCheckBox incognitoCheckBox = Components.checkBox( "Incognito" );
|
||||
incognitoCheckBox.setToolTipText( "Log in without saving any information." );
|
||||
incognitoCheckBox.setSelected( incognito );
|
||||
incognitoCheckBox.addItemListener( new ItemListener() {
|
||||
@Override
|
||||
@ -104,6 +115,7 @@ public class UnlockFrame extends JFrame {
|
||||
authenticationContainer.add( toolsPanel );
|
||||
for (JButton button : authenticationPanel.getButtons()) {
|
||||
toolsPanel.add( button );
|
||||
button.setBorder( BorderFactory.createEmptyBorder() );
|
||||
button.setMargin( new Insets( 0, 0, 0, 0 ) );
|
||||
button.setAlignmentX( RIGHT_ALIGNMENT );
|
||||
button.setContentAreaFilled( false );
|
||||
@ -121,13 +133,24 @@ public class UnlockFrame extends JFrame {
|
||||
} );
|
||||
}
|
||||
|
||||
void updateUser(User user) {
|
||||
void updateUser(@Nullable User user) {
|
||||
this.user = user;
|
||||
checkSignIn();
|
||||
}
|
||||
|
||||
boolean checkSignIn() {
|
||||
boolean enabled = user != null && !user.getFullName().isEmpty() && authenticationPanel.getMasterPassword().length > 0;
|
||||
String fullName = user == null? "": user.getFullName();
|
||||
char[] masterPassword = authenticationPanel.getMasterPassword();
|
||||
boolean enabled = !fullName.isEmpty() && masterPassword.length > 0;
|
||||
|
||||
if (fullName.isEmpty() || masterPassword.length == 0)
|
||||
identiconLabel.setText( " " );
|
||||
else {
|
||||
MPIdenticon identicon = new MPIdenticon( fullName, masterPassword );
|
||||
identiconLabel.setText( identicon.getText() );
|
||||
identiconLabel.setForeground( identicon.getColor().getAWTColor( MPIdenticon.BackgroundMode.DARK ) );
|
||||
}
|
||||
|
||||
signInButton.setEnabled( enabled );
|
||||
|
||||
return enabled;
|
||||
@ -170,7 +193,6 @@ public class UnlockFrame extends JFrame {
|
||||
}
|
||||
} );
|
||||
}
|
||||
|
||||
}
|
||||
} );
|
||||
}
|
||||
|
@ -126,12 +126,21 @@ public abstract class Components {
|
||||
};
|
||||
}
|
||||
|
||||
public static JLabel label(final String label) {
|
||||
return label( label, JLabel.LEADING );
|
||||
public static JLabel label(@Nullable String label) {
|
||||
return label( label, SwingConstants.LEADING );
|
||||
}
|
||||
|
||||
public static JLabel label(final String label, final int alignment) {
|
||||
return new JLabel( label, alignment ) {
|
||||
/**
|
||||
* @param horizontalAlignment One of the following constants
|
||||
* defined in <code>SwingConstants</code>:
|
||||
* <code>LEFT</code>,
|
||||
* <code>CENTER</code>,
|
||||
* <code>RIGHT</code>,
|
||||
* <code>LEADING</code> or
|
||||
* <code>TRAILING</code>.
|
||||
*/
|
||||
public static JLabel label(@Nullable final String label, final int horizontalAlignment) {
|
||||
return new JLabel( label, horizontalAlignment ) {
|
||||
{
|
||||
setFont( Res.controlFont().deriveFont( 12f ) );
|
||||
setAlignmentX( LEFT_ALIGNMENT );
|
||||
|
Loading…
Reference in New Issue
Block a user